一尘不染

查找列表中多个可能重复的整数中的任何一个

algorithm

给定的阵列n+1整数,每个范围1n,发现反复的整数。

面试时有人问我。这是我的答案:鸽洞原理说必须重复一遍。我尝试使用二进制搜索方法,所以我在Matlab中做到了这一点,因为这就是我所知道的:

top = 0;
bot = 0;
for i=1:n+1
  if P[i] > n/2 
    top = top+1;
  else
    bot = bot+1;
end

因此,我认为其中之一(topbot)必须n/2再次大于PhP。取该范围并重复。

我认为这是一个很好的解决方案,但面试官暗示这可以做得更好。请发布您知道的任何更好的解决方案。


阅读 230

收藏
2020-07-28

共1个答案

一尘不染

我不确定您如何定义“更好”,但是也许符合条件。至少与您的解决方案以及链表问题(双关语意味深远)的解决方案不同。

如果我们走一条路

n+1 --> array[n+1] --> array[array[n+1]] --> ...

然后,当且仅当array^k[n+1] = array^l[n+1]某某k != l(即,且仅当存在重复)时,此路径才包含一个循环。现在,该问题成为常见的链表问题,可以通过以下方式解决。

在第一个节点上启动两个粒子。让第一个粒子以单位速度移动,让第二个粒子以两倍单位速度移动。然后,如果有一个循环,第二个粒子将最终循环回到第一个粒子之后,最终它们将是相同的。为什么?好吧,如果您将粒子视为一个圆(一旦找到循环,它们将成为一个圆),则在每个时间单位,第二个粒子将比第一个靠近一个定向步。因此,它们最终必须碰撞。他们做到了,您发现了一个循环。要找到重复的值,只需让一个粒子静止不动,而另一个粒子再次运行循环,即可获得循环的长度。然后从头开始重新启动两个粒子,让一个向前移动循环的长度,

由于一些评论员感到愤怒,因为我没有包括如何在链表中查找循环的所有详细信息,所以现在就在这里。没有保证这不是越野车(毕竟是Matlab式的伪代码),但它至少应该解释一下这个想法。

%% STEP 1: find a point in the cycle of the linked list using a slow and fast particle
slow = n+1;
fast = n+1;
for i=1 to n+1
    slow = array[slow];
    fast = array[array[fast]];
    if (slow == fast)
        break;
end

%% STEP 2: find the length of the cycle by holding one particle fixed
length = 1;
fast = array[fast]
while fast != slow
    fast = array[fast];
    length = length+1;
end

%% STEP 3: find the repeated element by maintaining constant distance between particles
slow = n+1;
fast = n+1;
for i=1 to length
    fast = array[fast];
end
while fast != slow
    fast = array[fast];
    slow = array[slow];
end

%% STEP 4: return the repeated entry
return slow;

我从这里开始是n+1因为array[i]介于1和n之间,所以两个粒子都不会被发送回n+1。这样最多可以通过数组一次(虽然不是按顺序排列),并且可以跟踪两个粒子(慢和快)和一个整数(长度)。因此,空间为O(1),时间为O(n)。

2020-07-28