给定具有多个重复条目的阵列,重复输入O(N)时间和恒定空间

时间:2010-11-20 17:30:52

标签: java arrays algorithm

我们给出了一个大小为N的数组,其中包含0到N-2范围内的整数,包括0和N-2。

阵列可以有多个重复的条目。我们需要在O(N)时间和常量空间中找到一个重复的条目。

我正在考虑获取阵列中所有entires的乘积和总和,以及0到N-2范围内所有数字的乘积和总和。

然后,产品的总和和除法的差异将给出两个方程式。如果给出只有两个重复的条目,这种方法会起作用,但由于可能有两个以上,我认为我的方法失败了。

有什么建议吗?

编辑:数组是不可变的。我意识到这是一个重要的信息,我很抱歉我忘了早些提到这一点。

7 个答案:

答案 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)