您将获得一个 N 64位整数数组。 N可能非常大。你知道每个整数1..N在数组中出现一次,除了缺少一个整数和一个重复的整数。
编写线性时间算法以查找丢失和重复的数字。此外,您的算法应该在小的恒定空间中运行并保持数组不受影响。
答案 0 :(得分:36)
如果数组中存在所有数字,则总和为N(N+1)/2
。
通过在O(n)中对数组中的所有数字求和来确定实际总和,将其设为Sum(Actual)
。
缺少一个号码,请将其设为j
并重复一个号码,将其设为k
。这意味着
总和(实际)= N(N + 1)/ 2 + k-j
派生自
k =总和(实际)-N(N + 1)/ 2 + j
我们也可以计算数组中的平方和,总计为 如果所有数字都存在,n 3 / 3 + n 2 / 2 + n / 6。
现在我们可以用O(n)计算实际的平方和,让它为Sum(Actual Squares)
。
总和(实际平方)= n 3 / 3 + n 2 / 2 + n / 6 + k 2 - j 2
现在我们有两个方程式,我们可以用它来确定j
和k
。
答案 1 :(得分:29)
XOR技巧使用只读数组进行两次传递。
这避免了可能的整数溢出问题,求和和解的平方和。
让这两个数字为x
和y
,其中一个是缺失的数字,另一个是重复的。
XOR的所有元素以及1,2,...,N
。
结果为w = x XOR y
。
由于x
和y
不同,w
不为零。
选择w
的任何非零位。 x
和y
在这一点上有所不同。假设该位的位置为k
。
现在考虑将数组(和数字1,2,...,N
)分成两组,基于位置k的位是0还是1.
现在,如果我们计算两组元素的XOR(单独),结果必须是x
和y
。
由于分裂的标准只是检查是否设置了一个位,我们可以通过再次通过数组并有两个变量来计算两个集合中的两个XOR,每个变量都保存元素的XOR。到目前为止(和1,2,...N
)看到的每一组。最后,当我们完成后,这两个变量将保留x
和y
。
相关:
Finding missing elements in an array,可以推广为m出现两次,m缺失。
答案 2 :(得分:6)
使用您可以执行的related interview question的基本构思:
S1
)及其正方形(S2
)E1 = n*(n+1)/2
和E2 = n*(n+1)*(2n+1)/6
E1 - S1 = d - m
和E2 - S2 = d^2 - m^2
,其中d
是重复的数字,m
是缺失的数字。解决这个方程组,你会发现:
m = 1/2 ((E2 - S2)/(E1 - S1) - (E1 - S1))
d = 1/2 ((E2 - S2)/(E1 - S1) + (E1 - S1)) // or even simpler: d = m + (E1 - S1)
$S1 = $S2 = 0;
foreach ($nums as $num) {
$S1 += $num;
$S2 += $num * $num;
}
$D1 = $n * ($n + 1) / 2 - $S1;
$D2 = $n * ($n + 1) * (2 * $n + 1) / 6 - $S2;
$m = 1/2 * ($D2/$D1 - $D1);
$d = 1/2 * ($D2/$D1 + $D1);
答案 3 :(得分:5)
这是基于@Aryabhatta的想法的Java实现:
输入:[3 1 2 5 3]
输出:[3,4]
public ArrayList<Integer> repeatedNumber(final List<Integer> A) {
ArrayList<Integer> ret = new ArrayList<>();
int xor = 0, x = 0, y = 0;
for(int i=0; i<A.size(); i++) {
xor ^= A.get(i);
}
for(int i=1; i<=A.size(); i++) {
xor ^= i;
}
int setBit = xor & ~(xor-1);
for(int i=0; i<A.size(); i++) {
if((A.get(i) & setBit) != 0) {
x ^= A.get(i);
} else {
y ^= A.get(i);
}
}
for(int i=1; i<=A.size(); i++) {
if((i & setBit) != 0) {
x ^= i;
} else {
y ^= i;
}
}
for(int i=0; i<A.size(); i++) {
if(A.get(i) == x) {
ret.add(x);
ret.add(y);
return ret;
}
if(A.get(i) == y) {
ret.add(y);
ret.add(x);
return ret;
}
}
return ret;
}
答案 4 :(得分:3)
BrokenGlass提出的解决方案涵盖两个未知数的情况(对应于一个重复数字和一个缺失数字),使用两个公式:
和
公式分别得出订单n的generalized harmonic number为-1和-2。 (电源系列)
通过将n阶广义谐波数的值包含在-3中,该解可推广用于3个未知数。
要求 m 未知数(重复数和缺失数),请使用阶数n为-1到-m的m个广义谐波数。
Moron指出,此方法已在早期的StackOverflow Easy interview question got harder中讨论过。
答案 5 :(得分:3)
从字面上考虑leave the array untouched
要求(即,只要最终不改变,就可以暂时修改数组),可以建议面向编程的解决方案。
我认为数组大小 N 远小于 2 ^ 64 ,这是一个完全不切实际的内存量。因此,我们可以安全地假设N < 2^P
P << 64
(显着更小)。换句话说,这意味着数组中的所有数字都有一些高位未使用。因此,让我们只使用最高位作为标志,是否已在数组中看到该位置的索引。算法如下:
set HIGH = 2^63 // a number with only the highest bit set
scan the array, for each number k do
if array[k] < HIGH: array[k] = array[k] + HIGH // set the highest bit
else: k is the duplicate
for each i in 1..N do
if array[i] < HIGH: i is missing
else: array[i] = array[i] - HIGH // restore the original number
这是线性时间和非常少的计算
答案 6 :(得分:0)
long long int len = A.size();
long long int sumOfN = (len * (len+1) ) /2, sumOfNsq = (len * (len +1) *(2*len +1) )/6;
long long int missingNumber1=0, missingNumber2=0;
for(int i=0;i<A.size(); i++){
sumOfN -= (long long int)A[i];
sumOfNsq -= (long long int)A[i]*(long long int)A[i];
}
missingno = (sumOfN + sumOfNsq/sumOfN)/2;
reaptingNO = missingNumber1 - sumOfN;
答案 7 :(得分:-2)
假设该集合已被排序的Psuedo代码
missing = nil
duplicate = nil
for i = 0, i < set.size - 1, i += 1
if set[i] == set[i + 1]
duplicate = set[i]
else if((set[i] + 1) != set[i+1])
missing = set[i] + 1
if missing != nil && duplicate != nil
break
return (missing, duplicate)