说明在循环链表中查找循环起始节点的工作方式?


161

我知道Tortoise和Hare的会议总结了循环的存在,但是如何将乌龟移动到链接列表的开头,同时又将野兔保留在会场,然后一次移动两个步骤,使它们在循环的起点相遇呢?



人们不愿意超越这个问题的前两个答案。第三个答案很好。
displayName

Answers:


80

这是用于循环检测的弗洛伊德算法。您正在询问算法的第二阶段-找到一个节点是周期的一部分后,如何找到周期的起点

在弗洛伊德算法的第一部分中,野兔为乌龟的每一步移动了两步。如果乌龟和野兔相遇过,就会有一个循环,并且相遇点是循环的一部分,但不一定是循环中的第一个节点。

当乌龟和野兔相遇时,我们找到了最小的i(乌龟采取的步数),使得X i = X 2i。令mu代表从X 0到循环开始的步数,令lambda代表循环的长度。然后,i = mu +一个lambda,而2i = mu + b lambda,其中a和b是整数,表示乌龟和野兔在循环中跑了多少次。从第二个方程中减去第一个方程可得出i =(ba)* lambda,因此i是lambda的整数倍。 因此,X i + mu = X mu。X i代表乌龟和野兔的交汇点。如果您将乌龟移回起始节点X0,然后让乌龟和野兔以相同的速度继续前进,经过多步,乌龟将达到X mu,野兔将达到X i + mu = X mu,因此第二个相遇点表示乌龟的开始周期。


1
@Jim lewis当然,汇合点不是起点,但是正如我所说,将这两个中的一个移到链表的开头并以相同的速度移动将使它们在循环的起点相遇。
热情的程序员,2010年

6
@Jim Lewis如果您能解释一下将i作为循环长度的倍数如何导致mu作为第一个相遇点与循环起点之间的距离的结果,那将是很棒的。
热情的程序员,2010年

7
@Passionate:从起点开始执行mu步X_mu,直到循环的开始(根据mu的定义)。然后,如果您采取更多的步骤(其中i是循环长度的倍数),则最终返回循环开始:X_mu + i= X_mu。但是加法是可交换的,因此,这等效于从第i个步骤开始到第一个集合点X_i,然后再有第i个步骤回到X_mu循环的开始。
吉姆·刘易斯

2
@ankur:集合点是X_i,我们已经证明(我的答案的第三段)我必须是循环长度的倍数。在经过集合点多处步骤之后,您现在位于X_(i + mu)。但是我们已经证明X_(i + mu)= X_(mu + i)= X_mu,因为i的这一特殊属性,所以经过交汇点的mu步必须将您带到X_mu,即循环的开始。基本上是模块化算术,加上加法的可交换性质。
吉姆·刘易斯

28
我认为您的证明有一个小问题。由于汇合点i位于循环的某个点,因此我认为等式应为i = mu + k + a*lambda2i = mu + k + b*lambda,其中k从循环开始到汇合点的步数为。将两个方程式相减得出的结果相同。
Ivan Z. Siu

336

让我尝试用我自己的话来阐明http://en.wikipedia.org/wiki/Cycle_detection#Tortoise_and_hare提供的循环检测算法。

画画

这个怎么运作

让我们用乌龟和野兔(指针的名称)指向带有循环的列表的开头,如上图所示。

假设如果我们一次将乌龟移动1步,而每次又移动2步,它们最终将在某一点相遇。让我们首先证明这个假设是正确的。

该图显示了带有循环的列表。周期的长度为n,我们最初m距离周期仅一步之遥。也可以说汇合点k距离周期开始只有几步之遥,而当乌龟采取i全部步骤时,乌龟和野兔会合。(届时,哈雷本应采取2i全部步骤。)。

必须满足以下两个条件:

1) i = m + p * n + k

2) 2i = m + q * n + k

第一个说乌龟移动了i脚步,在这些i脚步中它首先进入了循环。然后它经过周期p时间为某个正数p。最后,它遍历k更多节点,直到遇到野兔。

野兔也是如此。它移动2i步骤,并在这些2i步骤中首先进入循环。然后它经过一个q正数的循环时间q。最后,它遍历k更多节点,直到遇到乌龟为止。

当野兔以两倍的乌龟速度行进时,当它们到达汇合点时,时间都是恒定的。

因此,通过使用简单的速度,时间和距离关系,

2 ( m + p * n + k ) = m + q * n + k

=> 2m + 2pn + 2k = m + nq + k 

=>  m + k = ( q - 2p ) n

在m,n,k,p,q中,前两个是给定列表的属性。如果我们可以证明至少有一组k,q,p值使该方程式成立,那么我们就可以证明这一假设是正确的。

一种这样的解决方案集如下:

p = 0

q = m

k = m n - m

我们可以验证这些值的工作方式如下:

m + k = ( q - 2p ) n  

=> m + mn - m = ( m - 2*0) n

=> mn = mn.

对于这个集合,i

i = m + p n + k

=> m + 0 * n + mn - m = mn.

当然,您应该看到这不一定是最小的。换句话说,乌龟和野兔可能已经相遇很多次了。但是,由于我们证明它们至少在某个时刻相遇,因此我们可以说该假设是正确的。因此,如果我们将其中一个移动1步,将另一个移动2步,那么他们将不得不见面。

现在我们可以转到算法的第二部分,即如何找到循环的起点。

循环开始

乌龟和野兔相遇之后,让我们将乌龟放回列表的开头,然后将野兔保持在它们相遇的位置(距离周期开始k步)。

假设是,如果我们让它们以相同的速度移动(两个速度都为1步),那么它们再一次相遇将是循环的开始。

让我们证明这个假设。

首先让我们假设一些先知告诉我们m是什么。

然后,如果我们让它们移动m + k步,则乌龟将不得不到达他们最初遇到的点(距循环起点k步-见图)。

以前我们显示过m + k = (q - 2p) n

因为m + k步长是周期长度n的倍数,所以野兔在同一时间将经历周期(q-2p)次,然后返回同一点(距周期开始k步长)。

现在,如果我们只让它们移动m + k步,而不是让它们移动m + k步,则乌龟将到达循环的开始。野兔距离完成(q-2p)次旋转还差k步。由于它在周期开始前k步开始,所以兔子必须到达周期开始。

结果,这说明他们将必须在第一次经过一定数量的步骤后才开始在循环中相遇(非常第一次),因为乌龟在经过m步之后才刚到达循环,它永远也看不到已经在其中的野兔了。周期)。

现在我们知道,移动它们直到遇到它们所需要的步数变成了从列表的开始到循环开始的距离m。当然,该算法不需要知道什么是m。它只会一次移动乌龟和野兔一步,直到它们见面为止。集合点必须是循环起点,步数必须是到循环起点的距离(m)。假设我们知道列表的长度,我们还可以计算从列表长度中减去m的循环长度。


1
我认为事实并非如此,当他们见面时才是起点,请看以下评论​​:stackoverflow.com/a/19209858/1744146 <br>如果我错了,请告诉我
MrA

解释的第一部分是完美无缺的。但是据我所知,第二部分有一个缺陷。您假设“某些甲骨文说m”,但是如果m已知,那么您已经开始了循环。当您不知道周期的开始在哪里时,如何假设答案呢?请告诉我。
Gopichand 2014年

1
@Gopichand再次阅读最后一段...您只是假设存在一些m(如果已经证明存在一个循环)..但您不知道m的值
Srinath

2
现在,这确实是一个奇妙的解释。这可能是目前整个互联网上最好的解释。
2015年

2
您的方程式m + k = (q - 2p) n可以进一步简化为m + k = q*n。这是因为乌龟经过的回圈数始终为零,因为野兔在不满足乌龟的情况下永远不会超越乌龟。想一想。
Arpit Jain

124

参考此图片:

在此处输入图片说明

慢速指针在会议之前经过的距离 = x + y

在会议之前fastPointer移动的距离 =(x + y + z)+ y = x + 2y + z

由于fastPointer行驶过程中 slowPointer的速度,时间是恒定的时,双方达成的交汇点。

因此,通过使用简单的速度,时间和距离关系2(x + y)= x + 2y + z => x + 2y + z = 2x + 2y => x = z

因此,通过将slowPointer移动到链表的开头,并使slowPointer和fastPointer一次移动一个节点,它们的覆盖距离相同

它们将到达链接列表中循环开始的位置。


10
这没有考虑在fastPointer进入循环之前,fastPointer循环n次的情况。使用l表示循环的长度。在会议之前,fastPointer所经过的距离 =(x + y + z)+ y = x + 2y + nl + z。结果关系为x = nl + z。
姚敬国

@JingguoYao:这是对此情况
displayName 2013年

2
此图过于简单。快速指针可以在慢速指针到达之前在循环中移动很多次。
沃伦·麦克沃伊

70

老和尚的简单且不恰当的答案解释了当快速奔跑者仅完成一个完整的循环时就找到循环的原因。在这个答案中,我将解释快跑者在慢跑者进入循环之前多次运行循环的情况。


使用同一张图片:在此处输入图片说明

假设快跑者在慢速和快速相遇之前已经运行了m次循环。这意味着:

  • 慢跑距离:x + y
  • 快速运行的距离:x + m(y + z)+ y,即它们相遇处的额外y

由于快速运行的速度是慢速运行速度的两倍,并且它们已经运行了相同的时间,这意味着如果将慢速运行的距离增加一倍,则可以得到快速运行的距离。从而,

  • 2(x + y)= x + m(y + z)+ y

求解x给出

x =(m-1)(y + z)+ z

在实际情况下,这意味着x = (m-1)个完整的循环运行+额外的距离z

因此,如果我们将一个指针放在列表的开头,而将另一个指针留在集合点,则以相同的速度移动它们将导致in循环指针完成m-1次循环运行,然后与另一个指针就在循环开始处。


7
一个疑问..它如何保证慢和快会在慢需要一个以上周期之前相遇?
锡拉吉

4
@siraj:慢速不会循环运行,快速会比慢速运行快,并且会在之前进入循环。并且保证他们会见面。如果慢是在J + 1和快速是与j,他们将现在J + 2满足而如果慢是j和快速的在J + 1,这意味着他们已经与j满足- 1
显示名

4
如果慢回绕循环,则该数学运算仍然有效:x +(y + z)m + y = 2(x +(y + z)n + y),其中n是慢回合在遇到之前进行慢回绕的次数。这解决了(m-2n-1)(y + z)+ z = x。这意味着从集合点开始,绕(m-2n-1)次,您回到集合点,然后再回到z,就在循环的起点。为此,它与从头节点开始并转到x节点相同。
mayas_mom '16

1
@mayas_mom:数学可能正在解决,但速度慢将永远无法解决。它总是会在开始时或中途被捕获。
displayName

4
x =(m-1)(y + z)+ z这可以归纳为环长度为y + z,并且由于仅关注位置。所以x =((m-1)(y + z))%(y + z))+ z这实际上是x = z;
anshul garg

10

这非常非常简单。您可以考虑相对速度。如果兔子移动了两个节点,而乌龟移动了一个节点,则相对于乌龟兔子移动了一个节点(假设乌龟处于静止状态)。因此,如果我们在循环链接列表中移动一个节点,那么我们肯定会在那时再次碰面。

在圆形链表中找到连接点后,现在问题简化为找到两个链表问题的交点。


8

图1

在第一次碰撞时,乌龟移动了m + k步,如上所示。野兔移动的速度是乌龟的两倍,这意味着野兔移动了2(m + k)步。从这些简单的事实,我们可以得出以下图形。

图1

在这一点上,我们将乌龟放回起点,并声明野兔和乌龟必须一次移动一步。根据定义,经过m步后,乌龟将处于循环的开始。兔子在哪里?

野兔也将在周期的开始。从第二张图中可以清楚地看出:当乌龟移回起点时,兔子进入了最后一个周期k步。经过m步,野兔将完成另一个循环并与乌龟相撞。


@WarrenMacEvoy我丝毫没有建议他们在起点见面。数字清楚地表明,他们在周期开始时再次会面。
skedastik

5

方法:

有两个指针:

  • 一种慢速指针,一次移动一个节点。
  • 快速指针,一次移动两个节点。

如果两个指针相遇,则证明存在循环。一旦它们相遇,其中一个节点将指向头部,然后两个节点都一次处理一个节点。他们将在循环开始时碰面。

理由: 当两个人沿着圆形轨道行走时,其中一个人的速度是另一个人的两倍,他们在哪里见面?正是他们开始的地方。

现在,假设快速奔跑者kn一步圈中领先一步。他们将在哪里见面?恰好n-k一步一步。当慢跑者掩盖了(n-k)脚步时,快跑者掩盖了k+2(n-k)脚步。(即,k+2n-2k步骤即2n-k步骤)。即(n-k)步骤(路径是循环的,我们不关心它们相遇之后的回合数;我们只对它们相遇的位置感兴趣)。

现在,快速跑步者是如何首先k迈出第一步的?因为慢跑者花了许多步骤才能到达循环的起点。因此,循环的起点距离头节点k步。

注意:两个指针相遇的节点k距离循环起点(在循环内)仅k几步之遥,而头节点也距循环起点仅几步之遥。因此,当我们使指针从bot处以等步速度前进时,这些节点将在循环开始时碰面。

我相信这很简单。请让我知道是否有任何歧义。


4
请在此处发布完整答案,而不仅仅是发布将来可能会中断的链接
Leeor

4

好吧,让我们假设兔子和乌龟在距周期开始k步的地方相遇,周期开始之前的步数为mu,周期的长度为L。

所以现在在集合点->

乌龟覆盖的距离= mu + a * L + k-公式1

(到达周期开始的步骤+覆盖周期的“ a”次迭代的步骤+从周期开始的k个步骤)(其中a是一些正常数)

野兔覆盖的距离= mu + b * L + k-公式2

(到达循环开始的步骤+覆盖循环的'b'迭代的步骤+从循环开始的k个步骤)(其中b是一些正常数,b> = a)

因此,野兔覆盖的额外距离为=公式2-公式1​​ =(ba)* L

请注意,该距离也等于乌龟到起点的距离,因为野兔的移动速度比乌龟快2倍。这可以等于“ mu + k”,如果我们不包括循环的多次遍历,这也是会议点到起点的距离。

因此,mu + k =(ba)* L

因此,从这一点开始的m步将导致回到循环的开始(因为已经从循环的开始采取了k步到达汇合点)。这可能发生在同一周期或任何后续周期中。因此,如果现在将乌龟移动到链表的开头,则将需要数μ步才能到达循环的起点,而野兔也需要数μ步才能到达循环的开始,因此它们都将在循环中相遇。周期的起点。

PS:老实说,我脑海中有一个与原始海报相同的问题,我读了第一个答案,他们确实清除了几件事,但我无法清楚地得出最终结果,因此我尝试以自己的方式做到这一点,发现它更容易理解。


他们通常在周期开始时不见面
Warren MacEvoy

3

在此处输入图片说明 形象功劳

调用距离是指针跟随的链接数,时间是算法移动慢速指针一个链接和快速指针两个链接所花费的迭代次数。长度为C的循环之前有N个节点,标记为循环偏移量k = 0至C-1。

要达到循环的开始,慢速需要N个时间和距离。这意味着快速在循环中需要N距离(N到达那里,N旋转)。因此,在时间N处,慢速为周期偏移k = 0,而快速为周期偏移k = N modC。

如果N mod C为零,则现在慢速和快速匹配,并且在时间N和周期位置k = 0处找到周期。

如果N mod C不为零,那么快必须赶上慢,而慢N在时间N是周期中落后C-(N mod C)的距离。

由于每1个慢速运动快速运动2,因此每次迭代将距离减少1,因此,这花费的时间与时间N的快慢运动距离之和为C-(N mod C)。由于慢速从偏移量0开始移动,因此这也是它们相遇的偏移量。

因此,如果N mod C为零,则阶段1在循环开始的N次迭代后停止。否则,阶段1在N + C-(N mod C)次迭代后在循环的偏移C-(N mod C)处停止。

// C++ pseudocode, end() is one after last element.

int t = 0;
T *fast = begin();
T *slow = begin();
if (fast == end()) return [N=0,C=0];
for (;;) {
    t += 1;
    fast = next(fast);
    if (fast == end()) return [N=(2*t-1),C=0];
    fast = next(fast);
    if (fast == end()) return [N=(2*t),C=0];
    slow = next(slow);
    if (*fast == *slow) break;
}

好吧,所以阶段2:慢需要N个步骤才能到达循环,在这一点上快(现在每时间步长移动1个)是(C-(N mod C)+ N)mod C =0。所以它们满足在阶段2之后的周期开始时。

int N = 0;
slow = begin();
for (;;) {
    if (*fast == *slow) break;
    fast = next(fast);
    slow = next(slow);
    N += 1;
}

为了完整起见,第3阶段通过在循环中再次移动来计算循环长度:

int C = 0;
for (;;) {
    fast = next(fast);
    C += 1;
    if (fast == slow) break;
}

:链接到Google文件来模拟算法docs.google.com/spreadsheets/d/...
沃伦MacEvoy

1
注意,如果N <= C,则迭代在C次迭代后停止。无论如何,它必须以少于N + C的步幅停止,并且不太可能在循环开始时停止。
沃伦·麦克沃伊

2

将问题简化为循环问题,然后回到最初的问题

我发现以下解释更加直观。

  1. 取两个指针(1 =龟和2从头部(开始=野兔)ø),1具有一个步长12具有一个步长2。想一想当1到达该周期的起始节点(A)的时刻。

    我们想回答以下问题“ 1在A中时2在哪里?”

    因此,OA = a是一个自然数(a >= 0)。但是可以通过以下方式编写:a = k*n + b,其中a, k, n, b are natural numbers

    • n =循环长度
    • k >= 0 =常数
    • 0 <= b <= n-1

    这意味着b = a % n

    例如:if a = 20n = 8=> k = 2并且b = 4因为20 = 2*8 + 4

    1覆盖的距离是d = OA = a = k*n + b。但同时有2个封面D = 2*d = d + d = OA + d = OA + k*n + b。这意味着当2在A中时,它必须覆盖k*n + b。正如你所看到的,k是圈数,但是那些圈后,2b远远A.所以,我们发现其中2是当1是A.我们叫这一点B,在这里AB = b

    在此处输入图片说明

  2. 现在,我们将问题简化为一个圆圈。问题是“集合点在哪里?” 。那C在哪里?

    在此处输入图片说明

    在每一步,2减少了从距离11(比如说米),因为1进一步从得到21,但在同一时间2变为接近12

    因此,相交将是当12之间的距离为零时。这意味着2减小了n - b距离。为了实现这一点,1将执行n - b步骤,而2将执行2*(n - b)步骤。

    因此,交点将n - b远离A(顺时针),因为这是1遇到2之前的距离。=> CA之间的距离是CA = b,因为AC = AB + BC = n - bCA = n - AC。不要以为AC = CA,因为AC距离不是微不足道的数学距离,所以它是AC之间的步数(其中A是起点,C是终点)。

  3. 现在,让我们回到初始模式。

    我们知道a = k*n + bCA = b

    我们可以使用2个新的指针1'1'',其中1'从头(O)开始,而1''从交点(C)开始。

    1'变为从ø1‘’从进Ç并且继续到结束k圈。因此,交点为一个

    在此处输入图片说明

    在此处输入图片说明


2

在此处输入图片说明

如果指针如图所示在点P处相遇,则距离Z + Y为点P,X + Y也是点P,这意味着Z = X。这就是为什么将一个指针从P移到起点(S)直到碰到另一个指针,这意味着为什么将一个相同的距离(Z或X)移到同一点M(与P的距离为Z,与S的距离为X)是为什么。循环的开始。简单!


1

通过以上所有分析,如果您是一个学习实例的人,我将尝试编写一个简短的分析和示例,以帮助解释其他人都试图解释的数学。开始了!

分析:

如果我们有两个指针,一个指针比另一个指针快,并且将它们一起移动,它们最终将再次相遇以指示一个周期,或者将它们归零以指示没有周期。

为了找到周期的起点,让...

  1. m 从头到周期开始的距离;

  2. d 是循环中的节点数;

  3. p1 是较慢的指针的速度;

  4. p2是更快的指针的速度,例如。2表示一次跨两个节点。

    观察以下迭代:

 m = 0, d = 10:
 p1 = 1:  0  1  2  3  4  5  6  7  8  9 10 // 0 would the start of the cycle
 p2 = 2:  0  2  4  6  8 10 12 14 16 18 20

 m = 1, d = 10:
 p1 = 1: -1  0  1  2  3  4  5  6  7  8  9
 p2 = 2: -1  1  3  5  7  9 11 13 15 17 19

 m = 2, d = 10:
 p1 = 1: -2 -1  0  1  2  3  4  5  6  7  8
 p2 = 2: -2  0  2  4  6  8 10 12 14 16 18

从上面的样本数据中,我们可以轻松地发现,只要速度更快的指针和速度较慢的指针相遇,它们就m距循环开始只有几步之遥。要解决此问题,请将较快的指针放回头部,然后将其速度设置为较慢的指针的速度。当他们再次见面时,节点就是周期的开始。


1

可以说

N[0] is the node of start of the loop, 
m is the number of steps from beginning to N[0].

我们有2个指针A和B,A的运行速度为1倍,B的运行速度为2倍,均从头开始。

当A达到N [0]时,B应该已经在N [m]中。(注意:A使用m步达到N [0],B应该进一步m步)

然后,A再跑k步撞到B,即A在N [k],B在N [m + 2k](注意:B应该从N [m]开始跑2k步)

分别在N [k]和N [m + 2k]处发生碰撞B,这意味着k = m + 2k,因此k = -m

因此,要从N [k]循环回到N [0],我们还需要m个步骤。

简而言之,我们只需要在找到碰撞节点后再执行m个步骤即可。我们可以有一个从头开始运行的指针和一个从碰撞节点运行的指针,它们将在m步后在N [0]处相遇。

因此,伪代码如下:

1) A increase 1 step per loop
2) B increase 2 steps per loop
3) if A & B are the same node, cycle found, then go to 5
4) repeat from 1
5) A reset to head
6) A increase 1 step per loop
7) B increase 1 step per loop
8) if A & B are the same node, start of the cycle found
9) repeat from 6

1

我不认为当他们见面时才是起点。但是可以,如果另一个指针(F)在之前的集合点,则该指针将在循环的末尾而不是循环的开始,并且指针(S)从列表的开始开始,它将结束于循环的开始。例如:

1->2->3->4->5->6->7->8->9->10->11->12->13->14->15->16->17->18->19->20->21->22->23->24->8

Meet at :16

Start at :8

public Node meetNodeInLoop(){

    Node fast=head;
    Node slow=head;

    fast=fast.next.next;
    slow=slow.next;

    while(fast!=slow){

        fast=fast.next;
        fast=fast.next;

        if(fast==slow) break; 

        slow=slow.next;
    }

    return fast;

}

public Node startOfLoop(Node meet){

    Node slow=head;
    Node fast=meet;

    while(slow!=fast){
        fast=fast.next;
        if(slow==fast.next) break;
        slow=slow.next;
    }

    return slow;
}

1

使用高中教授的相对速度的思想进行的简单解释-物理101 /运动学讲座。

在LinkedList中圈

  1. 假设从链表的起点到圆的起点的距离是x跳数。让我们将圆的起点称为点X(以大写字母表示-参见上图)。另外,假设圆的总大小为N个跃点。

  2. 野兔速度= 2 *乌龟速度。分别是1 hops/sec2 hops/sec

  3. 当乌龟到达圆的起点时X,野兔必须再x跳到Y图中的点。(因为野兔的距离是乌龟的两倍)。

  4. 因此,剩余弧的长度从X到Y顺时针为N-x。牛逼他也正好是兔子和乌龟之间被覆盖让他们能够满足的相对距离。假设这个相对距离会及时覆盖,t_m即会面的时间。相对速度(2 hops/sec - 1 hops/sec)1 hops/sec。因此,使用相对距离=相对速度X时间,我们得到t= N-x秒。因此,N-x要达到乌龟和野兔的汇合点。

  5. 现在,以N-x秒为单位的时间和1 hops/sec速度,早一点的乌龟X将遮盖Nx跃点到达交汇点M。因此,这意味着汇合点M位于= =的N-x逆时针跳动处X(这进一步暗示)=> x从点MX顺时针的距离还剩下。

  6. 但是x也是X从链表的开始到点的距离。

  7. 现在,我们不在乎x对应的跳数。如果我们在LinkedList的开始处放一只乌龟,在集合点M放一只乌龟,让他们跳/走,那么它们将在point会合X,这是我们需要的一点(或节点)。


1

使用图表进行操作会有所帮助。我试图不用方程式来解释问题。

  1. 如果我们让野兔和乌龟围成一圈,然后野兔跑两次乌龟,那么到了最后一圈,野兔乌龟将减半。在距离野兔乌龟两圈的最后一圈,他们俩会合。这适用于所有速度,例如,如果野兔跑了三遍,野兔1圈等于乌龟的1/3,那么在3圈结束时野兔乌龟将覆盖1圈并且它们会合。
  2. 现在,如果我们在循环前m步启动它们,则意味着更快的野兔正在循环中提前开始。因此,如果乌龟到达循环的起点,那么兔子会比循环前进m步,而当它们遇到时,兔子会在循环开始之前m步。

1

-循环前有k步。我们不知道k是什么,也不需要找出。我们只用k就可以抽象地工作。

-经过k步

----- T在循环开始时

----- H是进入循环的k步(他总共跑了2k,因此k进入循环)

**它们现在为loopsize-相距k

(请注意,k == K == mod(loopsize,k)-例如,如果一个节点进入5节点周期为2步,那么它也是7步,12步或392步,因此k的周期不大考虑到。

由于它们的移动速度是另一种的两倍,因此它们以每单位时间1步的速度互相追赶,因此它们会以循环大小-k相遇。

这意味着需要k个节点才能到达循环的开始,因此从头部到循环开始以及碰撞到循环开始的距离是相同的。

因此,现在在第一次碰撞后将T移回头部。如果您以1的速率移动,T和H将在循环启动时相遇。(两者均以k步为单位)

这意味着该算法为:

  • 从头部移动T = t.next和H.next.next直到它们发生碰撞(T == H)(有一个周期)

//通过计算循环长度来处理k = 0或T和H在循环开头相遇的情况

-用计数器在T或H周围移动来计算周期的长度

-将指针T2移动到列表的开头

-移动指针的循环步长

-将另一个指针H2移到头部

-将T2和H2串联移动,直到它们在循环开始时相遇

而已!


1

对此已经有了很多答案,但是我曾经想出一个对此更直观的图表。也许它可以帮助其他人。

对我来说主要的优点是:

  • T(乌龟)分为T1(预回路)和T2(回路)。 T =乌龟,H =野兔

  • H减去T,它们在视觉上重叠。剩下的(ħ - T = H”)等于Ť

  • 剩下的数学很简单。 从H中减去T在视觉上重叠的位置

-1

我知道已经有一个可以解决此问题的答案,但我仍将尝试以一种流畅的方式回答。假设:

The length of the Path is 'X+B' where 'B' is the length of the looped path and X of the non looped path. 
    Speed of tortoise : v
    Speed of hare     : 2*v 
    Point where both meet is at a distance 'x + b - k' from the starting point.

现在,让兔子和乌龟从开始的时间“ t”开始相遇。

观察结果:

如果,乌龟行进的距离= v * t = x +(bk)(例如)

然后,野兔经过的距离= 2 * v * t = x +(b-k)+ b(因为野兔已经穿过了环状部分)

现在,开会时间相同。

=> x + 2 * b-k = 2 *(x + b-k)

=> x = k

当然,这意味着未循环的路径的长度与循环的起点到两者相遇点的距离相同。


您不能假设乌龟在碰到它们时已经精确地移动了x + bk。另外,我不明白你是怎么得到x + 2 * bk的野兔距离的。
Plumenator

因为兔子会走过的环形部分已经一次不得不满足乌龟..我没有解释:/
n0nChun

-1

如果您考虑了汇合点后面的数学运算,实际上很容易证明他们俩都将在汇合点相遇。
首先,让m表示链表中循环的起点,n表示循环的长度。然后为了让野兔和乌龟见面,我们有:

( 2*t - m )%n = (t - m) %n, where t = time (at t = 0 , both are at the start)

用数学方式说明一下:

(2*t - m - (t - m) ) = 0 modulo n , which implies , t = 0 modulo n 

因此他们将在时间t见面,该时间应为周期长度的倍数。这意味着他们在某个地点相遇 (t-m) modulo n = (0-m) modulo n = (-m) modulo n

现在回到问题所在,如果您从链接列表的开头移动一个指针,从交点移动另一个指针,则经过m步后,我们的野兔(在循环内移动)将到达((-m) + m) modulo n = 0 modulo n因此,我们可以看到,在经过m步之后,它便进入了循环的起点,乌龟将在那里相遇,因为它将 从链表的起点开始经过m步。

顺便说一句,我们还可以这样计算它们的相交时间:条件t = 0 modulo n告诉我们它们将在一个周期长度的倍数处相遇,并且t应该大于m,因为它们在周期。因此,花费的时间将等于n的第一个倍数,大于m


他们不一定在周期开始时会面。
沃伦·麦克沃伊

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.