检测链表中循环开始的证明

时间:2010-10-17 10:18:06

标签: algorithm linked-list

从stackoverflow内外的几个帖子中,我已经知道如何检测链表中的循环,循环的长度。我还找到了如何检测循环开始的方法。

以下是再次参考的步骤。

检测循环:

有两个指针,通常称为野兔和乌龟。将野兔移动2步并将龟移动1.如果它们在某个时刻相遇,那么肯定会有一个循环,并且会合点显然在循环内。

寻找循环的长度:

将一个指针固定在会合点上,同时增加另一个指针直到它们再次相同。随着时间的推移增加一个计数器,满足时的计数器值将是周期的长度。

查找周期的开始

用一个指针开始列表,另一个指向会合点。现在将两者递增,并且满足点是循环的开始。我在纸上使用了一些案例验证了这种方法,但我不明白为什么它应该起作用。

有人可以提供数学证明,说明为什么这种方法有效吗?

6 个答案:

答案 0 :(得分:42)

如果您通过指向其第一个节点(列表

的指针来表示列表

enter image description here

检测循环的算法描述如下:

  1. 声明两个指针( pFast )和( pSlow )。
  2. pSlow pFast 指向列表
  3. 直到( pSlow ),( pFast )或两者都指向NULL:
    1. enter image description here
    2. enter image description here
    3. 如果enter image description here,则刚刚找到STOP作为循环。
  4. 如果达到了这一点(一个或两个指针都是NULL),那么列表中就没有循环。
  5. 让我们假设这个算法是正确的。 在此方案中,循环情况由下图表示:

    enter image description here

    注意除了指向循环开始的节点之外,每个节点都标有其目标数减1的节点。因此,如果一个节点标有 i 并且它不是循环的开头,那么它被标记为 i-1 的节点指向下一个元素。 / p>

    上面解释的算法可以被描述为循环,因为其主要步骤(3)是一组子步骤,其重复直到满足退出条件。这迫使我们在算法执行中的迭代次数( t )中表示 pFast pSlow

    enter image description here

    如果列表没有循环,则指针位置将在t的函数中描述为:

    enter image description here

    然而,有一个循环开始的节点,该函数停止描述指针的演变。假设此指针标有 m ,则该循环包含节点(即enter image description hereenter image description here),并将 t = 0 设置为enter image description here时的迭代值:

    enter image description here

    如果一个指针确实足以使用所描述的算法检测循环,那么它必须至少存在方程enter image description here的解决方案。

    当且仅当 t 的值有:

    时,此等式为真

    enter image description here

    这以函数enter image description here结束,该函数生成t的值,这些值是上述等式的有效解决方案:

    enter image description here

    enter image description here

    因此证明了一个慢指针和一个快速指针足以检测链表中的循环条件。

答案 1 :(得分:19)

如果您不使用会面点,您可以使“查找周期开始”证明更简单 让第二个指针不在“会合点”开始,但是M在第一个指针之前步进。 (其中M是循环的长度。)

这样,证据非常明显。当第一个指针到达循环的开始时,第二个指针将正好前进M:也就是在循环开始时。

答案 2 :(得分:8)

slowPointer在meet = 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一次移动一个节点,它们都具有相同的距离。

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

Diagram of proof

答案 3 :(得分:0)

我不认为这是真的,当他们遇到那是起点。但是,如果另一个指针(F)在之前的会合点,那么该指针将位于循环的结尾而不是循环的开始,并且从列表的开头开始的指针(S)将是在循环开始时结束。例如:

enter image description here

开始F作为Fast ans S比他们在9点结束时慢。 现在当我再次开始S时,它将在循环开始时到达,即7但f将在16 ..

如果我错了,请告诉我

答案 4 :(得分:0)

第二部分,声明“找到循环的开始,只需将一个指针移回列表的开头然后迭代它们直到它们相遇”是错误的!

只有当快速指针绕循环完全一次时才是正确的 - 即循环开始之前的部分比循环的长度短 - 但是如果它更长然后算法不起作用;你可能陷入无限循环。

尝试使用包含11个元素的链表,其中第11个指向第7个:

1 1 - > 3 2 - > 5 3 - > 7 4 - > 9 5 - > 11 6 - > 8 7 - > 10 8 - > 7 9 - > 9 9

- 检测到循环

向后移动一个以开始并递增它们: 1 9 - > 2 10 - > 3 11 - > 4 7 - > 5 8 - > 6 9 - > 7 10 - > 8 11 - > 9 7 - > 10 8 - > 11 9 - > 7 10 - > 8 11 - > ...

答案 5 :(得分:-2)

/* Algorithm : P2 is moving through the list twice as fast as P1.
   If the list is circular, it will eventually get around P1 and meet */

public boolean hasCycle()
{
  DoubleNode p1,p2;
  p1=p2=firstNode;    //Start with the first loop

  try
  {
    while (p2 != null)     //If p2 reaches end of linked list, no cycle exists
    {
        p1=p1.next;   //Move to next
        p2=p2.next.next; //Move to 2 steps next
        if(p1==p2)
           return true;     //p1 and p2 met, so this means that there is a cycle
    }
  }
  catch(NullPointerException npe)
  {
      //This means that p2 could not move forward
      return false;
  }

  return false;
}