拥有一台带有O(1)内存的机器。 我们想第一次传递n号(一个接一个), 我们再次排除两个数字,我们将其中的n-2个传递给机器。 写一个找到缺失数字的算法。 这是一个面试问题,我无法解决。
答案 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个缺失 - x
和y
,您可以执行以下操作:
使用高斯公式: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
。然后解析x
和y
的两个等式。
答案 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一样,但是有一个解释为什么起作用。
答案 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;
}