【问题标题】:Largest number formed from an array (understanding a solution)由数组形成的最大数(了解解决方案)
【发布时间】:2017-08-20 16:32:47
【问题描述】:

编写一个函数,给定一个非负整数列表,将它们排列成最大可能的数字。例如,给定 [0, 1, 2, 3],最大的形成数是 3210。

我理解的逻辑:

我们比较两个数字 XY(Y 附加在 X 的末尾)和 YX(X 附加在 Y 的末尾)。如果 XY 较大,则输出中 X 应该在 Y 之前,否则 Y 应该在之前。例如,让 X 和 Y 为 542 和 60。为了比较 X 和 Y,我们比较 54260 和 60542。由于 60542 大于 54260,我们将 Y 放在首位。我也可以为此编写代码。

让我惊讶的是这个解决方案:

#include <stdio.h>
#include<stdlib.h>

int swap(const void *c, const void *d) {
    int n1 = *(int*)c;
    int n2 = *(int*)d;

    int a = pow(10, floor(log10(n2)) + 1) * n1 + n2;
    int b = pow(10, floor(log10(n1)) + 1) * n2 + n1;

    if (n1 == 0) return 1;
    if (a < b) return 1;

    return 0;
}

int main() {
    int t = 0, tc = 0;

    scanf("%d", &t);
    for(tc = 1; tc <= t; tc++) {
        int n;
        scanf("%d",&n);
        int arr[n];

        for (int i = 0; i < n; i++) {
            scanf("%d", &arr[i]);
        }

        qsort(arr, n, sizeof(int), swap);

        for (int i = 0; i < n; i++)
            printf("%d", arr[i]);
        printf("\n");
    }
    return 0;
}

令我惊讶的是,它通过了所有测试用例。谁能给我解释一下这个逻辑?

【问题讨论】:

    标签: arrays algorithm sorting quicksort


    【解决方案1】:

    这完全符合您的描述:

    int a = pow(10, floor(log10(n2)) + 1) * n1 + n2;
    int b = pow(10, floor(log10(n1)) + 1) * n2 + n1;
    

    如果我们传入XY,那么aXYbYX

    如果要连接 2 和 34,则需要将 2 乘以 100(得到 200),然后加上 34(得到 234)。 100 是从哪里来的?它是 34 位数的 10 次方。为了得到位数,我们计算 34 的以 10 为底的对数并将其四舍五入。

    所以:

    log10(34) ~= 1.5
    floor(log10(34)) == 1
    floor(log10(34)) + 1 == 2
    

    10^2 = 100,所以现在我们知道在添加第二个之前要先乘以第一个数字。

    第二行以相反的顺序对变量执行相同的操作(计算YX 连接)。

    最后,如果 a

    if (a < b) return 1;
    

    编辑

    我不确定这条线在做什么:

    if (n1 == 0) return 1;
    

    我认为它可能会保护我们免受log10(0) 的影响。 (我不确定返回的是什么......数学结果是负无穷大。)

    基本上,比较器中的结果是“如果n1 为0,则将n2 放在首位”, 始终正确。 (我只是不是 100% 确定为什么需要它。)

    【讨论】:

      【解决方案2】:

      假设数组arr[] 是您的问题的解决方案,即它的元素以产生最大结果M 的方式排列。因此,交换任意数组元素ij 不会产生大于M 的结果。

      考虑在比较器函数swap 中比较任意索引ij,以及它们周围的数字:

      XXXXXXXX IIIIII XXXXXXXXXXXXXXXX JJJJJJ XXXXXXXXX
      -------- ------ ---------------- ------ ---------
      arr[...] arr[i]     arr[...]     arr[j]  arr[...]
      

      请注意,如果IIIIII 块在JJJJJJ 块之前排序,则无论X 块的内容如何,​​它都会继续在其前面排序。因此,当使用这种比较对整个数组进行排序时,单独比较 arr 的各个元素会产生最佳解决方案。

      您的比较器实现使用“十进制移位”执行此逻辑:如果您想在y 的数字后面添加x 的数字,您需要将y 的数字移位@987654337 中的位数@。 x中的位数可以确定为log10(x);通过将y 乘以 10k 来实现小数点左移 k 个位置。

      注意:这一行

      if (n1 == 0) return 1;
      

      应该在顶部,在你调用十进制对数之前。应该还有一行

      if (n2 == 0) return 0;
      

      确保我们不会将零传递给log10

      【讨论】:

        【解决方案3】:

        代码中所做的是,

        1. 输入数组
        2. 降序排列
        3. 输出

        输入和输出部分很容易理解。

        现在排序是使用接受比较函数的 qsort 完成的。在代码中,虽然函数名为 swap,但它实际上是一个比较函数 - 当第一个元素大于第二个元素时返回 1。否则返回 0。如 is 54 &gt; 45?is 45&gt;54?

        现在,为什么降序排序会给出应得的输出?我们来看一个例子:

        54 > 45 ,这意味着如果大数字在左侧,则数字更大。降序排序保留较大的数字。

        【讨论】:

          【解决方案4】:

          您已经很好地解释了为什么您发布的代码有效。但是,应该注意,只要任何数字的十进制移位版本超过最大可表示int,此方法就会发生溢出。如果我们假设一个 32 位的int,那么它有 10 个数字(2147483647),因此比较相对较小的数字(例如 32412 和 12345)会产生问题。

          作为替代方案,我们可以使用递归函数直接比较数字。让这两个数字为n1n2,分别为d1d2 数字。我们的比较函数需要处理三种情况:

          1. 如果d1 == d2我们直接比较n1n2,例如345和463

          2. 如果d1 &lt; d2 我们将n1n2 的高位d1 进行比较,例如对于 37 和 398,我们比较 37 和 39。如果它们相等,我们递归地将 n1 与 n2 的 d2-d1 低位数字进行比较。因此,对于 37 和 378,我们将比较 37 和 8。

          3. 如果 d1 &gt; d2 我们可以交换 n1n2 并按照案例 2 进行比较,但我们必须颠倒结果的顺序。

          这里有一些代码来说明。

          int swap(const void *c, const void *d) 
          {
             int n1 = *(int*)c;
             int n2 = *(int*)d;
          
             int d1 = numDigits(n1);
             int d2 = numDigits(n2);
             return compare0(n1, d1, n2, d2);
          }
          
          int compare0(int n1, int d1, int n2, int d2)
          {
              if (d1 == d2)
                  return n2 - n1;
              else if (d1 < d2)
                  return  compare1(n1, d1, n2, d2);
              else
                  return -compare1(n2, d2, n1, d1);
          }
          
          int compare1(int n1, int d1, int n2, int d2)
          {
              int pd = (int) pow(10, d2 - d1);
              int nh2 = n2 / pd;
              if (n1 == nh2)
                  return compare0(n1, d1, n2 % pd, d2 - d1);
              else
                  return nh2 - n1;
          }
          
          int numDigits(int n)
          {
              return (n == 0) ? 1 : 1 + (int) log10(n);
          }
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2019-09-24
            • 1970-01-01
            • 2011-04-12
            • 1970-01-01
            • 1970-01-01
            • 2021-02-17
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多