不,我不这么认为。您要么需要额外的空间才能通过分配给存储桶来对 O(n) 中的数据进行“排序”,要么需要就地排序,而不是 O(n)。
当然,如果你能做出某些假设,总会有窍门。例如,如果N < 64K 并且您的整数是 32 位宽,您可以在当前数组的顶部复用计数数组所需的空间。
换句话说,使用低 16 位存储数组中的值,然后使用高 16 位存储与索引匹配的值的计数。
让我们使用一个简化的示例,其中N == 8。因此数组的长度是 8 个元素,每个元素的整数都小于 8,尽管它们是 8 位宽。这意味着(最初)每个元素的前四位为零。
0 1 2 3 4 5 6 7 <- index
(0)7 (0)6 (0)2 (0)5 (0)3 (0)3 (0)7 (0)7
将计数存储到高四位的 O(n) 调整的伪代码是:
for idx = 0 to N:
array[array[idx] % 16] += 16 // add 1 to top four bits
举例来说,考虑存储 7 的第一个索引。因此,该赋值语句会将 16 添加到索引 7,从而增加 7 的计数。取模运算符是为了保证已经增加的值只使用低四位来指定数组索引。
所以数组最终变成:
0 1 2 3 4 5 6 7 <- index
(0)7 (0)6 (1)2 (2)5 (0)3 (1)3 (1)7 (3)7
然后你有你的新数组在常数空间,你可以使用 int (array[X] / 16) 来计算有多少 X 值。
但是,这非常曲折,需要前面提到的某些假设。这很可能是面试官正在寻找的那种狡猾程度,或者他们可能只是想看看未来的员工如何处理编码中的小林丸:-)
一旦你有了计数,找到总和为给定X 的对是一件简单的事情,仍然在 O(N) 中。基本方法是获得笛卡尔积。例如,再次考虑 N 是 8,并且您想要总和为 8 的对。忽略上面多路复用数组的下半部分(因为您只对计数感兴趣,所以您有:
0 1 2 3 4 5 6 7 <- index
(0) (0) (1) (2) (0) (1) (1) (3)
您基本上所做的是逐个遍历数组,得到总和为 8 的数字计数的乘积。
- 对于 0,您需要添加 8(不存在)。
- 对于 1,您需要加 7。计数的乘积是 0 x 3,因此没有给出任何结果。
- 对于 2,您需要加 6。计数的乘积是 1 x 1,因此出现了一次
(2,6)。
- 对于 3,您需要添加 5。计数的乘积是 2 x 1,因此会出现两次
(3,5)。
- 对于 4,这是一个特殊情况,因为您不能使用该产品。在这种情况下,这无关紧要,因为没有 4,但如果有 一个, 就不能成为一对。如果您配对的数字相同,则公式为(假设有
m)1 + 2 + 3 + ... + m-1。加上一点数学知识,结果是m(m-1)/2。
除此之外,您将与左侧的值配对,您已经完成了,所以您停止了。
所以你从
得到了什么
a b c d e f g h <- identifiers
7 6 2 5 3 3 7 7
是:
(2,6) (3,5) (3,5)
(c,b) (e,d) (f,d) <- identifiers
没有其他值加起来等于 8。
以下程序说明了这一点:
#include <stdio.h>
int arr[] = {3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5, 8, 9, 4, 4, 4, 4};
#define SZ (sizeof(arr) / sizeof(*arr))
static void dumpArr (char *desc) {
int i;
printf ("%s:\n Indexes:", desc);
for (i = 0; i < SZ; i++) printf (" %2d", i);
printf ("\n Counts :");
for (i = 0; i < SZ; i++) printf (" %2d", arr[i] / 100);
printf ("\n Values :");
for (i = 0; i < SZ; i++) printf (" %2d", arr[i] % 100);
puts ("\n=====\n");
}
上面那一点只是为了调试。执行桶排序的实际代码如下:
int main (void) {
int i, j, find, prod;
dumpArr ("Initial");
// Sort array in O(1) - bucket sort.
for (i = 0; i < SZ; i++) {
arr[arr[i] % 100] += 100;
}
我们完成了配对的代码:
dumpArr ("After bucket sort");
// Now do pairings.
find = 8;
for (i = 0, j = find - i; i <= j; i++, j--) {
if (i == j) {
prod = (arr[i]/100) * (arr[i]/100-1) / 2;
if (prod > 0) {
printf ("(%d,%d) %d time(s)\n", i, j, prod);
}
} else {
if ((j >= 0) && (j < SZ)) {
prod = (arr[i]/100) * (arr[j]/100);
if (prod > 0) {
printf ("(%d,%d) %d time(s)\n", i, j, prod);
}
}
}
}
return 0;
}
输出是:
Initial:
Indexes: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
Counts : 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
Values : 3 1 4 1 5 9 2 6 5 3 5 8 9 4 4 4 4
=====
After bucket sort:
Indexes: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
Counts : 0 2 1 2 5 3 1 0 1 2 0 0 0 0 0 0 0
Values : 3 1 4 1 5 9 2 6 5 3 5 8 9 4 4 4 4
=====
(2,6) 1 time(s)
(3,5) 6 time(s)
(4,4) 10 time(s)
而且,如果您检查输入的数字,您会发现这些对是正确的。