解释循环链表中查找循环开始节点的工作原理?

时间:2010-05-29 18:47:19

标签: algorithm linked-list cycle floyd-cycle-finding

据我所知,Tortoise和Hare的会议结束了循环的存在,但是如何在将野兔放在会场的同时将龟移到链表的开头,然后一步一步地移动两个步骤使它们在起点处相遇周期?

22 个答案:

答案 0 :(得分:291)

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

我在解释中参考了图drawing

工作原理

让我们有一只乌龟和一只野兔(指针的名字)指向一个循环的列表的开头。

让我们假设,如果我们一次一步地移动一步,一次只有两步,它们最终会在某一点上相遇。让我们先说明这个假设是正确的。

该图说明了一个带循环的列表。该周期的长度为n,我们最初距离周期m步。{1}}。另外,我们可以说会议点距离周期开始k步距离,并且在总共i步之后龟与野兔相遇。

必须遵守以下两个条件:

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.

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

现在我们可以进入算法的第二部分,即如何找到循环的开始。

周期开始

一旦乌龟和野兔见面,让我们把乌龟放回到清单的开头,并将野兔放在他们遇到的地方(距离循环开始的k步)。

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

让我们证明这个假设。

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

然后,如果我们让它们移动m + k步,那么龟必须到达它们最初遇到的点(距离循环开始k步 - 见图)。

之前我们展示了m + k = (q - 2p) n

由于m + k步长是周期长度n的倍数,因此,平均时间内的野兔将经历周期(q-2p)次并将返回到相同点(距周期开始k步) )。

现在,不是让他们移动m + k步,如果我们让他们只移动m步,乌龟就会到达循环开始。野兔将完成(q-2p)轮换。由于它在循环开始前开始了k步,因此野兔必须在循环开始时到达。

结果,这解释了他们必须在第一次经过一些步骤后才开始在周期开始见面(这是第一次因为陆龟刚刚到达m步后的周期而且它永远不会看到野兔已经在周期中了。)

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

答案 1 :(得分:113)

参考此图片:

enter image description here

slowPointer在见面之前的距离 = x + y

会见前fastPointer的距离 =(x + y + z)+ y                                                                                 = x + 2y + z

因为fastPointer以 double 的速度行进,所以当到达会面点时,slowPointer的速度和时间是恒定的

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

因此,通过将 slowPointer 移动到链接列表的开始,并使slowPointer和fastPointer一次移动一个节点,它们都具有相同的距离。< / p>

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

答案 2 :(得分:70)

这是Floyd's algorithm for cycle detection。您在询问算法的第二阶段 - 一旦您找到了一个循环的节点,如何找到循环的 start

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

当乌龟和野兔相遇时,我们发现了最小的i(乌龟采取的步数),使得X i = X 2i 。令mu表示从X 0 到循环开始的步数,让lambda表示循环的长度。然后i = mu + a lambda,2i = mu + b lambda,其中a和b是整数,表示乌龟和野兔绕循环的次数。减法 第二个方程式给出i =(b-a)* lambda,所以i是整数倍 兰达因此,X i + mu = X mu 。 X i 代表乌龟和野兔的交汇点。如果你将乌龟移回起始节点X 0 ,让乌龟和兔子继续以相同的速度,经过额外的步骤后乌龟将达到X mu ,野兔将达到X i + mu = X mu ,因此第二个汇合点表示循环的开始。

答案 3 :(得分:58)

Old Monk's simple and under-upvoted answer解释了当快速跑步者仅完成一个完整周期时找到周期。在这个答案中,我解释了当慢速跑步者在慢速跑步者进入循环之前多次运行循环时的情况。

使用相同的图片:enter image description here

让我们说快速跑步者在慢速和快速相遇之前已经运行了 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 运行循环,然后在循环开始时遇到另一个指针。

答案 4 :(得分:7)

Figure 1

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

Figure 1

此时,我们将乌龟移回到起点并宣布兔子和乌龟一次只能移动一步。根据定义,在 m 步骤之后,龟将处于循环的开始。会在哪里?

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

答案 5 :(得分:4)

非常简单。你可以考虑相对速度。如果兔子移动两个节点并且乌龟移动一个节点,则相对于兔子移动一个节点(假设休息时的乌龟)。因此,如果我们在循环链表中移动一个节点,我们肯定会在那时再次遇到。

找到圆形链表内的连接点后,现在问题减少到找到两个链表问题的交点。

答案 6 :(得分:2)

enter image description here image credit

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

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

如果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。因此,他们在第二阶段之后的周期开始时碰面。

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;
}

答案 7 :(得分:2)

好吧让我们假设野兔和乌龟在距离循环开始k步的点处相遇,循环开始前的步数是mu,循环的长度是L.

所以现在在会面点 - &gt;

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

(到达循环开始所采取的步骤+用于涵盖循环的'a'次迭代+从循环开始的k步骤所采取的步骤) (其中a是一些正常数)

hare = mu + b * L + k所涵盖的距离 - 等式2

(到达循环开始所采取的步骤+为了涵盖周期的'b'次迭代而采取的步骤+从循环开始的k步骤) (其中b是一些正常数,b> = a)

因此野兔所覆盖的额外距离=等式2 - 等式1 =(b-a)* L

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

因此,   mu + k =(b-a)* L

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

P.S。老实说,我和我原来的海报有同样的问题,我读了第一个答案,他们确实清除了一些东西,但我无法清楚地得到最终结果,所以我试着按照自己的方式去做,然后找到了它更容易理解。

答案 8 :(得分:2)

<强>方法

有两个指针:

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

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

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

现在,假设快速跑步者在k步跑圈中有n步的开头。他们会在哪里见面?正好在n-k步。当慢速跑步者已经完成(n-k)步时,快速跑步者将覆盖k+2(n-k)步。 (k+2n-2k步骤,即2n-k步骤)。即(n-k)步骤(路径是循环的,我们不关心它们遇到的轮数;我们只对他们遇到的位置感兴趣)。

现在,快速跑步者如何首先获得k步骤的先声夺人?因为慢跑者花了很多步才到达循环的开始。所以循环的开始是从头节点开始的k步。

注意:两个指针相遇的节点距离循环开始(在循环内)的步距为k,并且头节点也距离启动k步距离循环。因此,当我们从这些节点的机器人以相同的步长前进时,它们将在循环开始时相遇。

我认为这很简单。如果任何部分含糊不清,请告诉我。

答案 9 :(得分:1)

将问题减少为循环问题,然后回到初始问题

我发现以下说明更直观。

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

    我们想回答以下问题&#34;当1在A中时,2在哪里?&#34;

    因此,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

    例如:如果a = 20n = 8 =&gt; k = 2b = 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是圈数,但在这些圈后, 2 将远离A b 。所以,我们发现 1 在A中时,> 2 。让我们称之为B,其中AB = b

    enter image description here

  2. 现在,我们将问题缩小为一个圆圈。问题是&#34;会面点在哪里?&#34; 。那 C 在哪里?

    enter image description here

    在每一步中, 2 会减少 1 1的距离(让我们说米),因为 1 {strong> 2 与1越来越远,但同时 2 更接近 1 2

    因此,交点将是 1 2 之间的距离为零。这意味着 2 会缩短n - b距离。为了实现这一目标, 1 将执行n - b步骤,而 2 将执行2*(n - b)步骤。

    因此,交点将远离 A (顺时针)n - b,因为这是 1 所涵盖的距离,直到遇到 2 即可。 =&GT; C A 之间的距离为CA = b,因为AC = AB + BC = n - bCA = n - AC。不要认为AC = CA,因为AC距离不是一个微不足道的数学距离,而是 A C之间的步数(其中 A 是起点, C 是终点)。

  3. 现在,让我们回到初始架构。

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

    我们可以选择2个新指针 1&#39; 1&#39; ,其中 1&#39; 启动从头部( O )和 1&#39;&#39; 从交叉点开始( C )。

    1&#39; O 变为 A 1&#39;&#39; C A 并继续完成k圈。因此,交点是 A

    enter image description here

    enter image description here

答案 10 :(得分:1)

enter image description here

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

答案 11 :(得分:0)

通过以上所有分析,如果你是一个逐个学习的人,我试着写一个简短的分析和例子,帮助解释其他人试图解释的数学。我们走了!

<强>分析:

如果我们有两个指针,一个比另一个快,并将它们一起移动,它们最终会再次相遇以指示一个循环或null以指示没有循环。

要找到周期的起点,请......

  1. m是从头到开始的距离;
  2. d是周期中的节点数;
  3. p1是较慢指针的速度;
  4. p2是更快指针的速度,例如。 2表示一次通过两个节点的步骤。

    观察以下迭代:

  5.  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步。要解决此问题,请将较快的指针放回头部并将其速度设置为较慢指针的速度。当它们再次相遇时,节点就是循环的开始。

答案 12 :(得分:0)

让我们说,

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以1x速度运行,B以2x速度运行,两者都从头开始。

当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步之后在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

答案 13 :(得分:0)

我不认为这是真的,当他们遇到那是起点。但是,如果另一个指针(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;
}

答案 14 :(得分:0)

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

- k步后

----- T处于循环开始

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

**他们现在循环播放 - k分开

(注意k == K == mod(loopsize,k) - 例如,如果一个节点是进入5节点周期的2步,那么它也是7,12或392步,所以周期有多大k不参与。

由于他们以每单位时间1步的速度相互追赶,因为一个人的移动速度是另一个的两倍,他们将在loopize-k见面。

这意味着它将需要k个节点到达循环的开始,因此从头部到循环启动和碰撞到循环启动的距离是相同的。

所以现在在第一次碰撞后将T移回头部。如果你以每个1的速度移动,T和H将在cyclestart上相遇。 (两个步骤为k)

这意味着算法是:

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

//当k = 0或T和H在循环的头部遇到时,处理大小写 计算循环的长度

- 通过用计数器

移动周围的T或H来计算周期的长度

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

- 移动循环步骤的指针长度

- 将另一个指针H2移到头

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

就是这样!

答案 15 :(得分:0)

使用高中教授的relative velocity概念的简单解释 - 物理101 /运动学讲座。

Circle in LinkedList

  1. 假设从链表开始到圆圈开始的距离是x跳。让我们将圆圈的开头称为点X(在上限中 - 参见上图)。另外,我们假设圆的总大小为N跳。

  2. 野兔的速度= 2 *乌龟的速度。那就是1 hops/sec2 hops/sec

  3. 当乌龟到达圆圈X的开头时,野兔必须在图中的x点处进一步Y跳。 (因为野兔已经走过了两倍于乌龟的距离)。

  4. 因此,从X到Y顺时针的剩余弧的长度为N-x。 T 他也恰好是野兔和乌龟之间能够相遇的相对距离。假设这个相对距离将在时间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 hops逆时针方向X =(这进一步暗示)=&gt;从x点到M顺时针保持X距离。

  6. x也是从链接列表开头到达点X的距离。

  7. 现在,我们不关心跳数x对应的数量。如果我们在LinkedList的起点放置一只乌龟,在会合点M放一只乌龟并让它们跳/走,那么它们将在点X处相遇,这是点(或节点)我们需要的。

答案 16 :(得分:0)

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

  1. 如果我们让兔子和乌龟跑成一圈并且兔子跑了两次乌龟,那么在一圈结束时野兔龟就会变成一半。在野兔龟两圈结束时,他们将完成1圈,他们都会相遇。这适用于所有速度,如果野兔跑三次,野兔1圈等于龟的1/3,所以在3圈结束时,野兔乌龟将覆盖1圈并且它们相遇。
  2. 现在,如果我们在循环之前启动它们,那么这意味着更快的野兔在循环中开始。因此,如果乌龟到达循环的开始,野兔就会向前迈出几步,当它们相遇时,它将在循环开始之前的几个步骤。

答案 17 :(得分:0)

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

对我来说,主要的改变是:

  • T (乌龟)分为 T1 (循环前)和 T2 (循环内)。 T = tortoise, H = hare

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

  • 其余的数学很简单。 From H, subtract where T visually overlaps

答案 18 :(得分:0)

我看到大多数答案都对此给出了数学解释“如何将乌龟移动到链表的开头,同时将野兔保持在会面位置,然后一次移动一步使它们在开始时相遇循环点?"

下面的方法在幕后也做与弗洛伊德循环检测相同的事情,但原理很简单,但以 O(n) 内存为代价。

我想添加一个更简单的方法/理由来找到循环的开始。由于在任何地方都没有提到这个方法,我在这里测试了这个:https://leetcode.com/problems/linked-list-cycle-ii/ 并且它通过了所有的测试用例。

假设我们已经获得了 LinkedList 的头部引用。

     public ListNode detectCycle(ListNode head) {
     
            // Consider a fast pointer which hops two nodes at once.
            // Consider a slow pointer which hops one node at once.
            // If the linked list contains a cycle,
            // these two pointers would meet at some point when they are looping around the list.
            // Caution: This point of intersection need not be the beginning of the cycle.
            ListNode fast = null;
            ListNode slow = null;
            if (head != null) {
                if (head.next != null) {
                    fast = head.next.next;
                    slow = head;
                } else {
                    return null;
                }
            }
            while (fast != null && fast.next != null) {
                // Why do we need collection here? Explained below
                Set<ListNode> collection = new HashSet<>();
                if (fast == slow) {
                  // Once the cycle is detected, 
                     we are sure that there is beginning to the cycle.
                  // In order to find this beginning, 
                  // 1. move slow pointer to head and keep fast pointer at 
                        the meeting point.
                  // 2. now while moving slow and fast pointers through a 
                        single hop, store the slow reference in a collection.
                  // 3. Every time you hop the fast pointer, check the fast 
                        pointer reference exits in that collection.
                  // Rationale: After we moved slow pointer to the head, 
                     we know that slow pointer is coming behind the fast
                     pointer, since collection is storing all nodes from the 
                     start using slow pointer, there is only one case we get 
                     that fast pointer exists in the collection when slow 
                     pointer started storing the nodes which are part of the 
                     cycle. Because slow pointer can never go ahead of fast 
                     pointer since fast pointer already has an head-start, at 
                     the same time, the first occurence will always be of the 
                     starting point of the cycle because slow pointer can't 
                     go ahead of fast pointer to store other nodes in the 
                     cycle. So, the moment we first find fast pointer in that 
                     collection means, that is the starting point of the 
                     cycle.
                    slow = head;
                    collection.add(slow);
                    while (!collection.contains(fast)) {
                        slow = slow.next;
                        collection.add(slow);
                        fast = fast.next;
                    }
                    return fast;
                }
                fast = fast.next.next;
                slow = slow.next;
            }
            return null;
        }

答案 19 :(得分:-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

答案 20 :(得分:-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.

现在,让野兔和乌龟从一开始就在时间之后相遇。

观察:

如果,乌龟行进的距离= v * t = x +(b-k)(比如说)

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

现在,会议时间相同。

=&GT; x + 2 * b - k = 2 *(x + b - k)

=&GT; x = k

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

答案 21 :(得分:-1)

假设您的指针在点y和z相交。

n和m是分别在满足条件之前更快和更慢的指针所经过的循环数。

有关其余证明,请参考图像。 Find the starting point of loop in linked list