我知道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;
}
我的疑问如下:
这段代码是如何工作的?
for(i=0; i < k; i++){ if(arr[i] != brr[i]) return brr[i] - arr[i]; }
它正在比较
arr
和brr
的内容,但是它如何返回值以便以这种方式对值进行排序?即使它返回值,也应按递增顺序打印它们。为什么它以降序显示它们?
这个比较函数很奇怪。
为了理解它是如何工作的,让我们开始考虑 qsort 期望什么:它期望一个接受 2 个数字的函数,a
和b
,以及
0 的整数,如果第一个参数分别被视为小于、等于或大于第二个参数。
在这种情况下,正如我们将看到的,它实际上返回相反的结果,也就是说,如果第二个参数分别被视为小于、等于或大于第一个参数,则它返回一个小于、等于或大于 0 的整数。这只是因为此代码的作者希望按降序而不是升序排序。
也就是说,这个比较功能是一个特殊的功能:让我们看看它的作用。虽然它适用于数字(它接受void*
,这是一个通用指针,但随后它会强制转换两个参数来int*
并取消引用它们),但它不会在数字上比较它们,甚至不会以经典的字母数字方式进行比较。 相反,它指示两个数字一旦转换为字符串,必须以哪个顺序连接以生成数字序列,这些数字序列被解释为数字,产生最大可能的结果(这由另一个函数的名称暗示,largestNumber
)。它通过将必须首先取的数字指示为最大数字来做到这一点。这听起来很复杂,但一些例子会澄清。
让我们取数字 3 和 4。如果你把它们当作字符串("3"
和"4"
),你可以用两个不同的顺序连接它们,得到"34"
或"43"
。现在,将这些字符串转换回数字:哪个更大?显然,34 <43,如果你想要最大的数字 (43),你必须把 4 作为第一个数字,3 作为第二个数字。在这种情况下,该函数将告诉您最大数字为 4。它通过返回一个正数(在本例中为 1)来实现此目的。
让我们取 30 和 4。如果将它们连接起来,则会得到"304"
或"430"
。由于 304 <430,该函数告诉您最大数字为 4。因此,从这个角度来看,4> 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
检查一个的长度,然后循环检查每个数字,从第一个开始。如果数字在ab
和ba
之间相同,它什么也不做,只是转到下一个;如果不同,它会比较它们并返回brr
的数字减去arr
的数字。例如,在比较 3 和 30 时,arr
将包含 330 和brr
303。在第一次迭代(当i==0
时)它会检查第一个数字:两者都是 3,所以你不能选择哪个数字更高。移动到第二个数字:3> 0,所以arr
>brr
,所以函数必须返回一个负数,确实如此:它返回0 - 3 = -3
。
因此,该函数比较a
和b
并返回:
- 如果它们按此顺序连接产生最大数字,则为负数 (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];