找到两个缺少的数字

时间:2012-04-18 22:08:07

标签: c++ algorithm math discrete-mathematics

拥有一台带有O(1)内存的机器。 我们想第一次传递n号(一个接一个), 我们再次排除两个数字,我们将其中的n-2个传递给机器。 写一个找到缺失数字的算法。 这是一个面试问题,我无法解决。

7 个答案:

答案 0 :(得分:11)

可以使用O(1)内存完成。

您只需要几个整数来跟踪一些运行总和。整数不需要log n位(其中n是输入整数的数量),它们只需要2b + 1位,其中b是单个输入整数中的位数。

当您第一次阅读流时,添加所有数字及其所有正方形,即每个输入数字n,执行以下操作:

sum += n
sq_sum += n*n

然后在第二个流上为两个不同的值sum2和sq_sum2做同样的事情。现在做以下数学:

sum - sum2 = a + b
sq_sum - sq_sum2 = a^2 + b^2

(a + b)(a + b) = a^2 + b^2 + 2ab
(a + b)(a + b) - (a^2 + b^2) = 2ab
(sum*sum - sq_sum) = 2ab

(a - b)(a - b) = a^2 + b^2 - 2ab
               = sq_sum - (sum*sum - sq_sum) = 2sq_sum - sum*sum
sqrt(2sq_sum - sum*sum) = sqrt((a - b)(a - b)) = a - b
((a + b) - (a - b)) / 2 = b
(a + b) - b = a

在所有中间结果中需要2b + 1位,因为您要存储两个输入整数的乘积,并且在一种情况下将这些值中的一个乘以2。

答案 1 :(得分:3)

假设这些数字的范围是1..N,其中有2个缺失 - xy,您可以执行以下操作:

使用高斯公式:sum = N(N+1)/2

sum - actual_sum = x + y

使用数字产品:product = 1*2..*N = N!

product - actual_product = x * y

解析x,y,你有你缺少的数字。

简而言之 - 遍历数组并总结每个元素以获得actual_sum,将每个元素相乘得到actual_product。然后解析xy的两个等式。

答案 2 :(得分:3)

使用O(1)内存无法完成。

假设您有一个恒定的k位内存 - 那么您的算法可以有2^k种可能的状态。

但是 - 输入不受限制,并假设(2^k) + 1 (2^k) + 1个{{1}}不同的问题案例可能有{{1}}个答案,您将在两个问题中返回相同的答案两次答案,因此你的算法是错误的。

答案 3 :(得分:2)

一旦我读完这个问题,就会想到以下内容。但是上面的答案表明O(1)内存是不可能的,或者数字范围应该有限制。告诉我,如果我对这个问题的理解是错误的。好的,所以这里是

你有 O(1)记忆 - 这意味着你有恒定的记忆量

当第一次传递 n 数字时,只需将它们添加到一个变量中并继续将它们相乘。因此,在第一遍结束时,您将获得2个变量 S1 P1 中所有数字的总和和乘积。你到目前为止使用了2变量(如果你在内存中读取数字,则为+1)。

(n-2)号码第二次传递给您时,请执行相同的操作。将(n-2)数字的总和和乘积存储在2个其他变量 S2 P2 中。到目前为止,您已使用了4个变量(如果您在内存中读取数字,则为+1)。

如果两个缺失的数字是 x y ,那么

x + y = S1 - S2
x*y = P1/P2;

两个变量中有两个方程式。解决它们。

所以你使用了一定量的内存(独立于n)。

答案 4 :(得分:1)

void Missing(int arr[], int size)
{
  int xor = arr[0]; /* Will hold xor of all elements */
  int set_bit_no;  /* Will have only single set bit of xor */
  int i;
  int n = size - 2;
  int x = 0, y = 0;

  /* Get the xor of all elements in arr[] and {1, 2 .. n} */
  for(i = 1; i < size; i++)
    xor ^= arr[i];  
  for(i = 1; i <= n; i++)
    xor ^= i;   

  /* Get the rightmost set bit in set_bit_no */
  set_bit_no = xor & ~(xor-1);

  /* Now divide elements in two sets by comparing rightmost set
   bit of xor with bit at same position in each element. */
  for(i = 0; i < size; i++)
  {
    if(arr[i] & set_bit_no)
      x = x ^ arr[i]; /*XOR of first set in arr[] */
    else
      y = y ^ arr[i]; /*XOR of second set in arr[] */
  }
  for(i = 1; i <= n; i++)
  {
    if(i & set_bit_no)
      x = x ^ i; /*XOR of first set in arr[] and {1, 2, ...n }*/
    else
      y = y ^ i; /*XOR of second set in arr[] and {1, 2, ...n } */
  }

  printf("\n The two repeating missing elements are are %d & %d ", x, y);
}

答案 5 :(得分:0)

请查看下面的解决方案链接。它说明了XOR方法。 该方法比上面说明的任何方法更有效。 可能与上面的Victor一样,但是有一个解释为什么起作用。

Solution here

答案 6 :(得分:0)

这是简单的解决方案,不需要任何二次公式或乘法:

让我们说B是两个缺失数字的总和。

两个遗漏的数字之一是: (1,B-1),(2,B-1)...(B-1,1)

因此,我们知道这两个数字之一将小于或等于B的一半。

我们知道我们可以计算B(两个缺失数字之和)。

因此,一旦有了B,我们将找到列表中所有小于或等于B / 2的数字之和,然后从(1到B / 2)的总和中减去该数字以获得第一个数字。然后,通过从B中减去第一个数字来获得第二个数字。在下面的代码中,rem_sum为B。

public int[] findMissingTwoNumbers(int [] list, int N){
    if(list.length == 0 || list.length != N - 2)return new int[0];

    int rem_sum = (N*(N + 1))/2;
    for(int i = 0; i < list.length; i++)rem_sum -= list[i];

    int half = rem_sum/2;
    if(rem_sum%2 == 0)half--; //both numbers cannot be the same
    int rem_half = getRemHalf(list,half);

    int [] result = {rem_half, rem_sum - rem_half};
    return result;
}
private int getRemHalf(int [] list, int half){
    int rem_half = (half*(half + 1))/2;
    for(int i = 0; i < list.length; i++){
        if(list[i] <= half)rem_half -= list[i];
    }
    return rem_half;
}