【问题标题】:How does this compare function work?这个比较功能是如何工作的?
【发布时间】:2017-07-29 14:03:18
【问题描述】:

我了解到compare 函数正在对值进行排序,以便以降序显示数字组合。

例如:给定[3, 30, 34, 5, 9],最大的形成数是9534330

 int compare(const void *a1,const void *b1){
        int a = *(int*)a1;
        int b = *(int*)b1;
        int i=0;
        char arr[10000]={0};
        char brr[10000]={0};
        sprintf(arr, "%d%d", a, b);
        sprintf(brr, "%d%d", b, a);
        int k = strlen(arr);
        for(i=0; i < k; i++){
            if(arr[i] != brr[i])
                return brr[i] - arr[i];
        }
        return b-a;
    }
    char* largestNumber(const int* A, int n1) {
        char *ans = (char*) calloc(10000000,sizeof(char));
        int i=0, count=0;
         qsort(A, n1, sizeof(int), compare);
        if(A[0] == 0){  
            ans[0] = '0'; ans[1]=0; return ans;
        }
        for(i=0; i<n1; i++){
            int k = A[i];
            // printf("%d ", k);
            count += sprintf(ans+count, "%d", k);
        }
        // printf("\n");
        ans[count] = 0;
        return ans;
    }

我的疑惑如下:

  1. 这段代码是如何工作的?

    for(i=0; i < k; i++){
        if(arr[i] != brr[i])
            return brr[i] - arr[i];
    }
    

    它正在比较arrbrr 的内容,但它是如何返回值以便以这种方式对值进行排序的?

  2. 即使它返回值,它们也应该按升序打印。为什么按降序显示?

【问题讨论】:

  • 注:1)可以使用int k = sprintf(arr, "%d%d", a, b);“sprintf函数返回写入数组的字符数,不计算终止的空字符,如果发生编码错误,则返回负值。” 2)char arr[10000]={0}; 相当极端,也许是char arr[100]={0};? 3) 整个for(i=0; i &lt; k; i++){ if(arr[i] != brr[i]) ... 看起来类似于strcmp()
  • 如果您使用sprintf 并在提供给qsortcompare 函数内循环,则可能需要永远和一天。该函数旨在对值进行简单比较,或者如果struct 中有层次结构,则进行几个比较。但是您的compare 函数似乎正试图接管qsort 的工作。也许你应该重新考虑算法。该解决方案可能会受益于递归方法。
  • @WeatherVane 不同意 - 这仍然是一个 n log n 问题。sprintf() 只是形成要比较的字典值。
  • @chux,也许我在你发帖的时候又加了一句。
  • 分配一千万个字符的空间来记录答案似乎有点过分,尽管我想这在一定程度上取决于n1 的大小和范围在输入数组的元素上。

标签: c compare qsort


【解决方案1】:

这个比较函数很奇怪。
为了理解它是如何工作的,让我们开始考虑what qsort expects:它需要一个接受两个数字的函数,ab,以及哪个

如果第一个参数分别被认为小于、等于或大于第二个参数,则返回一个小于、等于或大于 0 的整数。

在这种情况下,正如我们将看到的,它实际上返回相反的结果,即,如果考虑 second 参数,它返回一个小于、等于或大于 0 的整数分别小于、等于或大于第一个。这只是因为这段代码的作者想要按降序而不是按升序排序。

也就是说,这个比较函数是一个特殊的函数:让我们看看它做了什么。虽然它适用于数字(它接受void*,这是一个通用指针,但随后它将两个参数都转换为int* 并取消引用它们),它不会对它们进行数字比较,甚至不会以经典的字母数字方式进行比较。 相反,它指示两个数字在转换为字符串后必须以何种顺序连接以生成数字序列,该数字序列被解释为数字,产生最大可能的结果(这由另一个函数的名称暗示,largestNumber)。它通过将必须首先采取的数字指示为最大数字来做到这一点。这听起来很复杂,但一些例子会澄清。

让我们使用数字 3 和 4。如果您将它们视为字符串("3""4"),您可以将它们以两种不同的顺序连接起来,得到 "34""43"。现在,将这些字符串转换回数字:哪个更大?显然,34

让我们用 30 和 4 来代替。如果你连接它们,你会得到"304""430"。由于 304 30,这与数字排序相反。

如果你取 3 和 30,两个可能的串联是 "330""303",由于 330 > 303 你必须先取 3,函数告诉你 3 > 30。同样,它是相反的数字排序。

如果取 30 和 30,则必须在 "3030""3030" 之间进行选择,它们是相同的,在这种情况下,函数返回 0。

现在是一个棘手的情况:如果比较 3 和 33,两个“字符串化”数字是相同的("333""333"),所以我们期望 0。实际上在这种情况下,函数告诉你最大的数是第二个……不过没关系,结果是一样的。

究竟是如何进行比较的?首先,使用sprintf 将数字转换为字符串。考虑到 2 个字符串(我们称它们为 "ab""ba")必须具有相同的长度,因此它使用 strlen 检查一个字符串的长度,然后循环检查每个数字,从第一个数字开始。如果abba 之间的数字相同,它什么也不做,只是转到下一个;如果不同,则比较它们并返回brr 的数字减去arr 的数字。例如,当比较 3 和 30 时,arr 将包含 330 和 brr 303。在第一次迭代时(i==0)它会检查第一个数字:两者都是 3,所以你不能选择哪个数字更高。移动到第二个数字:3 > 0,所以arr > brr,所以函数必须返回一个负数,确实如此:它返回0 - 3 = -3

所以,函数比较 ab 并返回:

  • 如果通过按 (a, b) 顺序连接它们产生最大数,则为负数;
  • 0,如果数字相同,那么它们的顺序无关紧要;
  • 如果它们必须以相反的顺序 (b, a) 连接以产生最大数字,则为正数。

此表显示了经典数字排序、经典字母数字排序和这种自定义排序(我称之为“最大字符串化数字排序”)的结果

 a   b    Numeric sort   Alphanumeric sort   Largest stringified number sort
 3   4      3 <   4          3 <   4            34 <    43  --> return  1 >  0 -->  3  <  4
30   4     30 >   4         30 <   4           304 <   430  --> return  1 >  0 -->  30 <  4
 3  30      3 <  30          3 <  30           330 >   303  --> return -3 <  0 -->  3  > 30
 3  31      3 <  31          3 <  31           331 >   313  --> return -2 <  0 -->  3  > 31
 3  32      3 <  32          3 <  32           332 >   323  --> return -1 <  0 -->  3  > 32
 3  33      3 <  33          3 <  33           333 ==  333  --> return 30 >  0 -->  3  < 33
 3  34      3 <  34          3 <  34           334 <   343  --> return  1 >  0 -->  3  < 34
30  30     30 == 30         30 == 30          3030 == 3030  --> return  0 == 0 --> 30 == 30

至于为什么它们是按降序打印的,仅仅是因为这行:

return brr[i] - arr[i];

如果您想按递增顺序查看它们,请将其更改为:

return arr[i] - brr[i];

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-07-12
    • 1970-01-01
    • 2022-11-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多