Arrays 在大小为N的整数数组中查找总和为X的对,该数组的元素范围为0到N-1
这是一个面试问题。我们有一个大小为N的整数数组,包含0到N-1之间的元素。一个数字可能出现两次以上。目标是找到总和为给定数字X的对 我使用了一个辅助数组,该数组包含主数组的元素数,然后根据辅助数组重新排列主数组,以便对主数组进行排序,然后搜索对 但面试官希望空间复杂度保持不变,所以我告诉他对数组进行排序,但这是一个非对数时间复杂度的解决方案。他想要O(n)解决方案Arrays 在大小为N的整数数组中查找总和为X的对,该数组的元素范围为0到N-1,arrays,algorithm,sorting,Arrays,Algorithm,Sorting,这是一个面试问题。我们有一个大小为N的整数数组,包含0到N-1之间的元素。一个数字可能出现两次以上。目标是找到总和为给定数字X的对 我使用了一个辅助数组,该数组包含主数组的元素数,然后根据辅助数组重新排列主数组,以便对主数组进行排序,然后搜索对 但面试官希望空间复杂度保持不变,所以我告诉他对数组进行排序,但这是一个非对数时间复杂度的解决方案。他想要O(n)解决方案 有没有什么方法可以在没有任何额外空间的情况下用O(n)来做这件事?没有,我不这么认为。您或者需要额外的空间,以便能够通过分配给buc
有没有什么方法可以在没有任何额外空间的情况下用O(n)来做这件事?没有,我不这么认为。您或者需要额外的空间,以便能够通过分配给bucket来“排序”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
例如,考虑存储7的第一个索引。因此,该赋值语句将在索引7中添加16,增加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
基本上,你要做的是一步一步地遍历数组,得到总数为8的数字的乘积
- 对于0,您需要添加8(它不存在)
- 对于1,您需要添加7。计数的乘积是0 x 3,因此没有任何结果
- 对于2,您需要添加6。计数的乘积为1 x 1,因此出现一次
(2,6)
- 对于3,您需要添加5。计数的乘积为2 x 1,因此出现两次
(3,5)
- 对于4,这是一个特例,因为你不能使用该产品。在这种情况下,这并不重要,因为没有4s,但如果有4s,就不能成为一对。当您配对的数字相同时,公式是(假设有
个)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
而且,如果检查输入数字,您会发现对是正确的。这可以通过在O(N)时间内将输入数组转换为“就地”计数器列表来完成。当然,这假设输入数组不是不变的。不需要对每个数组元素中未使用的位进行任何额外的假设 从以下预处理开始:尝试将每个数组的元素移动到由元素值确定的位置;将该位置上的元素也移动到由其值确定的位置;继续,直到:
- 下一个元素被移动到该循环开始的位置
- 无法移动下一个元素,因为它已位于与其值对应的位置(在本例中,将当前元素放置到该循环开始的位置)
k=2
(值小于k
的数组部分的大小由计数器替换)。对k=2、4、8……应用以下程序
k.处查找元素。。2k-1
处于“正确”位置的,用计数器替换,初始值为“1”k。。2k-1
带值2。。k-1
更新位置2.处的相应计数器。。k-1
并将值重置为零0。。2k-1
带值k。。2k-1
更新位置k.处的相应计数器。。2k-1
并将值重置为零0.位置最多有两个计数器。。2k-1
的值可能大于k-1
。但这可以通过为每个索引存储两个附加索引并将这些索引处的元素作为计数器而不是值来缓解
生成计数器数组后,我们可以将计数器对相乘(其中对应的索引对总和为
X
)以获得所需的对计数。字符串排序是n log n,但是如果您可以假设数字是有界的(您可以这样做,因为您只对总和为特定值的数字感兴趣)可以使用基数排序。基数排序取O(kN)
0 1 2 3 4 5 6 7 <- index
(0) (0) (1) (2) (0) (1) (1) (3)
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
#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)