我们给出了一个大小为N的数组,其中包含0到N-2范围内的整数,包括0和N-2。
阵列可以有多个重复的条目。我们需要在O(N)时间和常量空间中找到一个重复的条目。
我正在考虑获取阵列中所有entires的乘积和总和,以及0到N-2范围内所有数字的乘积和总和。
然后,产品的总和和除法的差异将给出两个方程式。如果给出只有两个重复的条目,这种方法会起作用,但由于可能有两个以上,我认为我的方法失败了。
有什么建议吗?
编辑:数组是不可变的。我意识到这是一个重要的信息,我很抱歉我忘了早些提到这一点。
答案 0 :(得分:9)
这是一个很好的治疗方法。在解决这个问题之前,它会解决一些更容易的问题。
http://aperiodic.net/phil/archives/Geekery/find-duplicate-elements.html
它包含一个解决方案,用于何时可以修改输入数组,另一个用于无法修改输入数组的解决方案。
链接永远消亡的简要总结:数组索引从0 ... N-1开始,数组值运行0 ... N-2。因此,每个数组元素都可以被视为数组本身的索引(或“指针”):元素i
“指向”元素ra[i]
,ra[i]
指向ra[ra[i]]
等等。通过反复遵循这些指针,我最终必须进入一个循环,因为如果不重新访问某个节点或其他节点,我们肯定不会永远不会永远。
现在,最后一个元素N-1未被任何其他元素指向。因此,如果我们从那里开始并最终进入一个循环,那么沿途的某个地方必须有一个可以从两个不同的地方到达的元素:我们第一次采取的路线,以及作为周期一部分的路线。像这样:
N-1 -> a1 -> a2 -> a3
^ \
/ v
a6 <- a5 <- a4
在这种情况下,a2可以从两个不同的地方到达。
但是可以从两个不同的地方到达的节点正是我们正在寻找的节点,数组中的副本(两个不同的数组元素包含相同的值)。
接下来的问题是如何识别a2,答案是使用Floyd's cycle-finding algorithm。特别是它告诉我们在O(N)时间和O(1)空间中“开始”循环。
答案 1 :(得分:3)
假设我们被允许更改数组,当你通过数组在那个“位置”上时通过数组交换每个元素(例如,如果当前元素是curr然后用[curr]交换)但是如果[curr]已经有了curr然后你知道curr是重复的。
a = array...
for i = 0; i < length(a); i++
curr = a[i]
if a[curr] == curr:
return duplicate curr
swap(a[i], a[curr])
# Now a[curr] == curr and so if it happens again we know it is a duplicate.
这将是O(n)和恒定空间。
答案 2 :(得分:1)
扫描阵列并将每个元素添加到集合中。如果该项目中已存在该项目 - 您有一个骗局。
答案 3 :(得分:1)
初始化一个大小为N-2的位数组,所有条目都为0.每个索引将代表0到N-2范围内的所有项目。
通过设置bitarray[number] == 1
循环遍历数组并将项目添加到比特阵列。如果element已经包含1,那么您已经添加了元素,立即返回。
如果在没有找到重复的情况下到达数组的末尾,则返回-1。
答案 4 :(得分:1)
灵感来自this SO Question我想我会选择使用wikipedia中找到的O(n)(虽然不一定很快)算法就地对阵列进行排序(很好的图形排序演示)找到here),然后遍历结果数组,找到下一个数字等于当前数字的位置。
答案 5 :(得分:0)
尝试使用其他数据结构。某些数据结构(如HashSet)在添加或搜索时不会遍历当前元素,从而保留O(n)。
HashSet hSet = new HashSet();
for(int i = 0; i < array.length(); i++){
if(hSet.contains(array[i])
return array[i];
else
hSet.add(array[i]);
}
return -1;
虽然我不确定这会满足你的记忆要求,但是,对于你想要的第二次遍历的地方排序,extraneon以前的帖子可能更多你想要的
答案 6 :(得分:0)
(抱歉还不能添加评论....)
@Blastfurnace 啊..很好地捕获for循环需要先检查
if a[i] == i:
continue # Don't swap with yourself!
如果数组是不可变的,那么你可以保持跟随元素,即跳过a [i] == i然后从[i]转到a [a [i]]。这将触及一个循环然后我们可以使用“如何检测链表中的循环”解决方案(保持2个指针,一个以速度1移动,另一个移动到2,当它们都遇到你时,你知道你已经达到了循环)。
如果我们可以更改数组,然后将其保持不变,那么我们可以作弊:) 从i = 0开始,将a [a [i]]变为负整数(如果它已经是负数),如果它已经是负数,那么我们知道元素a [i]已被访问过两次。在返回之前将所有底片转回正面(对于0使用MIN_INT)