This was an interview question.
I was given an array of n+1
integers from the range [1,n]
. The property of the array is that it has k (k>=1)
duplicates, and each duplicate can appear more than twice. The task was to find an element of the array that occurs more than once in the best possible time and space complexity.
After significant struggling, I proudly came up with O(nlogn)
solution that takes O(1)
space. My idea was to divide range [1,n-1]
into two halves and determine which of two halves contains more elements from the input array (I was using Pigeonhole principle). The algorithm continues recursively until it reaches the interval [X,X]
where X
occurs twice and that is a duplicate.
The interviewer was satisfied, but then he told me that there exists O(n)
solution with constant space. He generously offered few hints (something related to permutations?), but I had no idea how to come up with such solution. Assuming that he wasn't lying, can anyone offer guidelines? I have searched SO and found few (easier) variations of this problem, but not this specific one. Thank you.
EDIT: In order to make things even more complicated, interviewer mentioned that the input array should not be modified.
答案 0 :(得分:12)
取最后一个元素(x)。
将元素保存在x(y)位置。
如果x == y,则发现重复。
用x。覆盖位置x。
指定x = y并继续执行步骤2.
您基本上是对数组进行排序,因为您知道元素必须插入的位置。 O(1)额外空间和O(n)时间复杂度。你只需要小心索引,为简单起见我假设第一个索引是1(不是0)所以我们不必做+1或-1。
编辑:不修改输入数组
该算法基于我们必须找到置换周期的入口点的想法,然后我们还发现了一个重复的(为简单起见,再次使用基于1的数组):
示例:
2 3 4 1 5 4 6 7 8
参赛作品:8 7 6
置换周期:4 1 2 3
我们可以看到副本(4)是循环的第一个数字。
查找排列周期
测量周期长度
查找循环的入口点
3个主要步骤都是O(n)和顺序因此总体复杂度也是O(n),空间复杂度是O(1)。
上面的例子:
x取以下值:8 7 6 4 1 2 3 4 1 2
a取以下值:2 3 4 1 2
b取以下值:2 4 2 4 2
因此c = 4(是的,有5个数字,但c只在步骤时增加,而不是最初)
x取以下值:8 7 6 4 | 1 2 3 4
y采用以下值:| 8 7 6 4
x == y == 4最后,这是一个解决方案!
评论中要求的示例2:3 1 4 6 1 2 5
输入周期:5 1 3 4 6 2 1 3
测量周期长度:
a:3 4 6 2 1 3
b:3 6 1 4 2 3
c = 5
找到切入点:
x:5 1 3 4 6 | 2 1
y:| 5 1
x == y == 1是一个解决方案
答案 1 :(得分:5)
这是一个可能的实现:
function checkDuplicate(arr) {
console.log(arr.join(", "));
let len = arr.length
,pos = 0
,done = 0
,cur = arr[0]
;
while (done < len) {
if (pos === cur) {
cur = arr[++pos];
} else {
pos = cur;
if (arr[pos] === cur) {
console.log(`> duplicate is ${cur}`);
return cur;
}
cur = arr[pos];
}
done++;
}
console.log("> no duplicate");
return -1;
}
for (t of [
[0, 1, 2, 3]
,[0, 1, 2, 1]
,[1, 0, 2, 3]
,[1, 1, 0, 2, 4]
]) checkDuplicate(t);
&#13;
它基本上是@maraca提出的解决方案(输入太慢了!)它具有恒定的空间要求(对于局部变量),但除此之外只使用原始数组进行存储。在最坏的情况下应该是O(n)
,因为一旦找到重复,该过程就会终止。
答案 2 :(得分:2)
如果允许非破坏性地修改输入向量,那么这很容易。假设我们可以通过否定它来标记输入中的元素(这显然是可逆的)。在这种情况下,我们可以按如下方式进行:
注意:以下假设向量从1开始编制索引。因为它可能从0开始索引(在大多数语言中),所以可以实现“索引i处的标记项”和“否定项目” index i-1“。
答案 3 :(得分:0)
这取决于您(您的应用)可以使用的工具。目前存在许多框架/库。对于C ++标准版的例子,您可以使用std :: map&lt;&gt; ,正如马拉卡所说。
或者如果你有时间可以自己实现二叉树,但是你需要记住,元素的插入与通常的数组相同。在这种情况下,您可以在特定情况下优化对重复项的搜索。
二叉树探索。参考: https://www.wikiwand.com/en/Binary_tree