在线性时间和常量空间中查找数组中缺少的和重复的元素

时间:2011-04-23 21:03:23

标签: algorithm language-agnostic

您将获得一个 N 64位整数数组。 N可能非常大。你知道每个整数1..N在数组中出现一次,除了缺少一个整数和一个重复的整数。

编写线性时间算法以查找丢失和重复的数字。此外,您的算法应该在小的恒定空间中运行并保持数组不受影响。

来源:http://maxschireson.com/2011/04/23/want-a-job-working-on-mongodb-your-first-online-interview-is-in-this-post/

8 个答案:

答案 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

现在我们有两个方程式,我们可以用它来确定jk

答案 1 :(得分:29)

XOR技巧使用只读数组进行两次传递。

这避免了可能的整数溢出问题,求和和解的平方和。

让这两个数字为xy,其中一个是缺失的数字,另一个是重复的。

XOR的所有元素以及1,2,...,N

结果为w = x XOR y

由于xy不同,w不为零。

选择w的任何非零位。 xy在这一点上有所不同。假设该位的位置为k

现在考虑将数组(和数字1,2,...,N)分成两组,基于位置k的位是0还是1.

现在,如果我们计算两组元素的XOR(单独),结果必须是xy

由于分裂的标准只是检查是否设置了一个位,我们可以通过再次通过数组并有两个变量来计算两个集合中的两个XOR,每个变量都保存元素的XOR。到目前为止(和1,2,...N)看到的每一组。最后,当我们完成后,这两个变量将保留xy

相关:

答案 2 :(得分:6)

使用您可以执行的related interview question的基本构思:

  • 汇总所有数字(我们称之为S1)及其正方形(S2
  • 计算预期的数字总和,无需修改,即E1 = n*(n+1)/2E2 = n*(n+1)*(2n+1)/6
  • 现在您知道E1 - S1 = d - mE2 - 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提出的解决方案涵盖两个未知数的情况(对应于一个重复数字和一个缺失数字),使用两个公式:

sum1

sum2

公式分别得出订单n的generalized harmonic number为-1和-2。 (电源系列)

通过将n阶广义谐波数的值包含在-3中,该解可推广用于3个未知数。

sum3

要求 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)