我在尝试解决这个问题时遇到了很多麻烦,而麻烦的根源在于创建O(n)
复杂度的算法。这是我正在努力解决的问题:
长度为
A
的数组n
包含[0, .., n - 1]
范围内的整数。但是,它只包含n - 1
个不同的数字。因此,其中一个数字丢失,另一个数字重复。编写一个Java方法,将A
作为输入参数并返回缺少的数字;该方法应该在O(n)
中运行。例如,当
A = [0, 2, 1, 2, 4]
时,oddOneOut()
应该返回3
;当A = [3, 0, 0, 4, 2, 1]
时,oddOneOut()
应该返回5
。
显然,这是一个使用O(n2)
算法解决的简单问题,(很可能是O(n)
,我只是没有看到它!)。我试图用各种方法解决它,但无济于事。我试图用Java解决它,但是如果你更习惯解决Python,那也没关系。
提前谢谢你......
答案 0 :(得分:34)
假设缺少的数字为x
,副本为y
。如果添加所有数字,总和将为:
(n - 1) * n / 2 - x + y
从上面可以找到(x - y)
.....(1)
同样,对数字的平方求和。总和将是:
(n - 1) * n * (2 * n - 1) / 6 - x2 + y2
从上面你得到(x2 - y2)
....(2)
(2) / (1) gives (x + y).....(3)
(1)+(3)给出2 * x
,您可以找到x
和y
。
请注意,在此解决方案中, O(1)
额外存储空间并且 O(n)
时间复杂度。上面的其他解决方案是不必要的O(n)
额外存储。
混合C / C ++中的代码更清晰:
#include <stdio.h>
int findDup(int *arr, int n, int& dup, int& missing)
{
int sum = 0;
int squares = 0;
for (int i = 0; i < n; i++) {
sum += arr[i];
squares += arr[i] * arr[i];
}
sum = (n - 1) * n / 2 - sum; // x - y
squares = (n - 1) * n * (2 * (n - 1) + 1) / 6 - squares; // x^2 - y^2
if (sum == 0) {
// no duplicates
missing = dup = 0;
return -1;
}
missing = (squares / sum + sum) / 2; // ((x^2 - y^2) / (x - y) + (x - y)) / 2 = ((x + y) + (x - y)) / 2 = x
dup = missing - sum; // x - (x - y) = y
return 0;
}
int main(int argc, char *argv[])
{
int dup = 0;
int missing = 0;
int a[] = {0, 2, 1, 2, 4};
findDup(a, sizeof(a) / sizeof(int), dup, missing);
printf("dup = [%d], missing = [%d]\n", dup, missing);
int b[] = {3, 0, 0, 4, 2, 1};
findDup(b, sizeof(b) / sizeof(int), dup, missing);
printf("dup = [%d], missing = [%d]\n", dup, missing);
return 0;
}
输出:
dup = [2], missing = [3]
dup = [0], missing = [5]
一些python代码:
def finddup(lst):
sum = 0
sumsq = 0
missing = 0
dup = 0
for item in lst:
sum = sum + item
sumsq = sumsq + item * item
n = len(a)
sum = (n - 1) * n / 2 - sum
sumsq = (n - 1) * n * (2 * (n - 1) + 1) / 6 - sumsq
if sum == 0:
return [-1, missing, dup]
missing = ((sumsq / sum) + sum) / 2
dup = missing - sum
return [0, missing, dup]
found, missing, dup = finddup([0, 2, 1, 2, 4])
if found != -1:
print "dup = " + str(dup) + " missing = " + str(missing)
print finddup([3, 0, 0, 4, 2, 1])
输出:
dup = 2 missing = 3
[-1, 0, 0]
答案 1 :(得分:16)
迭代数组两次:那仍然是O(n)。创建一个临时的布尔数组(或一个Java BitSet)来保存你得到的数字。第二次进行循环时,检查布尔数组中是否有孔。
答案 2 :(得分:4)
使用哈希集并单次传递以检测哪个数字是重复的。在同一次迭代中,跟踪所有数字的累积总和。
现在计算所有数字不同的预期总数:n * (n - 1) / 2
。减去你找到的总数。您将留下“缺失”数字减去副本。添加副本以获得答案。
由于哈希表访问是常量时间,并且我们使用单次传递,因此这是O(n)
。 (请注意,单次传递不是绝对必要的:Martijn指出固定数量的传递仍然是线性复杂度是正确的。)
答案 3 :(得分:1)
这可能是有意义的,虽然我不确定在什么条件下(如果有的话)它表现最好。我们的想法是,我们将每个元素移动到数组中的正确位置(0
以索引0等),直到它变得清楚什么是缺失的和什么是额外的。
def findmissing(data):
upto = 0
gap = -1
while upto < len(data):
#print data, gap
if data[upto] == upto:
upto += 1
continue
idx = data[upto]
if idx is None:
upto += 1
continue
data[upto], data[idx] = data[idx], data[upto]
if data[upto] == data[idx]:
print 'found dupe, it is', data[upto]
data[upto] = None
gap = upto
upto += 1
elif data[upto] is None:
gap = upto
return gap
if __name__ == '__main__':
data = range(1000)
import random
missing = random.choice(data)
print missing
data[missing] = data[0]
data[0] = random.choice(data[1:])
random.shuffle(data)
print 'gap is', findmissing(data)
这是O(n),因为每个步骤 递增upto
或将值移动到数组中的“正确”位置,并且每个步骤事情只能发生n
次。