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

发布于 2024-09-27 15:28:00 字数 425 浏览 1 评论 0 原文

从 stackoverflow 内部和外部的几篇文章中,我开始知道如何检测链表中的循环,即循环的长度。我还找到了如何检测循环开始的方法。

这里再次提供步骤供参考。

检测循环:

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

查找循环长度:

将一个指针固定在交汇点,同时增加另一个指针,直到它们再次相同。随着您的进行而增加计数器,并且相遇时的计数器值将是周期的长度。

找到循环的开始

将一个指针指向列表的开头,并将另一个指针保留在交汇点。现在将两者都增加 1,交汇点就是循环的开始。我用纸上的一些案例验证了该方法,但我不明白为什么它应该有效。

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

From several posts inside stackoverflow and outside, I have come to know how to detect cycles in a linked list, the length of a cycle. I also found the method on how to detect the start of the loop.

Here are the steps again for reference.

Detecting Loop:

Have two pointers, classically called hare and tortoise. Move hare by 2 steps and tortoise by 1. If they meet at some point, then there is surely a cycle and the meeting point is obviously inside the cycle.

Finding length of Loop:

Keep one pointer fixed at meeting point while increment the other until they are same again. Increment a counter as you go along and the counter value at meet will be the length of cycle.

Find the start of cycle

Take one pointer to start of the list and keep the other at the meeting point. Now increment both by one and the meet point is the start of loop. I verified the method using some cases on paper, but I don't understand why it should work.

Can someone, provide a mathematical proof as to why this method works?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

夏日浅笑〃 2024-10-04 15:28:00

如果您通过指向第一个节点的指针 (列表) 来表示列表

在此处输入图像描述

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

  1. 声明两个指针(pFast)和(pSlow)。
  2. 使pSlowpFast指向列表
  3. 直到 (pSlow)、(pFast) 或两者都指向 NULL:
  4. 在此处输入图像描述
  5. 在此处输入图像描述
  6. 如果在此处输入图像描述,则停止,因为刚刚发现了循环。
  7. 如果已到达这一点(一个或两个指针均为 NULL),则列表中不存在循环。

让我们假设这个算法是正确的。
在此方案中,循环情况由下图表示:

在此处输入图像描述

请注意除第一个节点外的每个节点如何指向循环的开始,用其目标的编号减一来标记。因此,如果一个节点标记为 i 并且它不是循环的开始,则标记为 i-1 的节点将其指向下一个元素。

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

在此处输入图像描述

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

在此处输入图像描述

但是,有一个循环开始的节点,并且该函数停止描述指针的演变。假设该指针标记有 m,则循环包含节点(即 在此处输入图像描述在此处输入图像描述),并在 < 时将 t=0 设置为迭代值img src="https://i.sstatic.net/DTxNP.gif" alt="在此处输入图像描述"> 然后:

“在此处输入图像描述”

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

当且仅当 t 的值使得:

在此处输入图像描述时, 该方程才成立。

这以一个函数结束,在此处输入图像描述,该函数生成 t 的值,这些值是上述方程的有效解:

在此处输入图像描述

enter image description here

由此证明,一个慢指针和一个快指针足以检测链表中的循环情况。

If you represent a list by a pointer to its first node (list)

enter image description here

The algorithm to detect loops is described as follows:

  1. Declare two pointers (pFast) and (pSlow).
  2. Make pSlow and pFast point to list.
  3. Until (pSlow), (pFast) or both point to NULL:
    1. enter image description here
    2. enter image description here
    3. If enter image description here, then STOP as a loop has just been found.
  4. If this point has been reached (one or both two pointers are NULL) then there are no loops in the list.

Lets assume that this algorithm is correct.
In this scheme, a loop situation is represented by the following diagram:

enter image description here

Note how every node, except the one pointing to the begining of a loop, is labeled with the number of its target minus one. So, if one node is labeled with i and it is not the begining of a loop, then it is pointed as next element by the node labeled with i-1.

The algorithm explained above can be described as a loop since its main step (3) is a set of sub-steps which repeated until the exit condition is satisfied. That forces us to represent pFast and pSlow in function of the iteration number in the algorithm execution (t).

enter image description here

If the list hadn’t have loops, pointers positions would be described in function of t as:

enter image description here

However there is a node where the loop starts and that function stops describing the evolution of the pointers. Assuming that this pointer is tagged with m, that the loop contains nodes (that is enter image description here and enter image description here), and setting t=0 as iteration value when enter image description here then:

enter image description here

If one pointer is indeed enough to detect loops using the algorithm described, then it must exist at least a solution to the equation enter image description here.

This equation is true if and only if there is a value for t that makes:

enter image description here

This ends in a function,enter image description here which generates values of t that are valid solutions to the equation described above:

enter image description here

enter image description here

Thus It is proved that one slow pointer and one fast pointer are enough to detect loop conditions in a linked list.

老娘不死你永远是小三 2024-10-04 15:28:00

如果您不使用交汇点,您可以使“找到周期开始”证明变得更简单。
让第二个指针不是从“交汇点”开始,而是在第一个指针之前 M 步。 (其中M是循环的长度。)

这样,证明就非常明显了。当第一个指针到达循环开始时,第二个指针将正好领先 M 步:也在循环开始处。

You can make your 'Find the start of cycle' proof simpler if you don't use meeting point.
Let second pointer start not at the 'meeting point', but M steps ahead of first pointer. (Where M is the length of the loop.)

This way, proof is pretty obvious. When the first pointer reaches start of the loop, second will be exactly M steps ahead: also at the start of the loop.

倾城花音 2024-10-04 15:28:00

SlowPointer 相遇前移动的距离 = 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一次移动一个节点,它们都有相同的覆盖距离。

它们将到达链表中循环开始的点。

证明图

Distance travelled by slowPointer before meeting = x + y

Distance travelled by fastPointer before meeting = (x + y + z) + y = x + 2y + z

Since fastPointer travels with double the speed of slowPointer, and time is constant for both when the reach the meeting point.

So by using simple speed, time and distance relation 2(x+y)= x+2y+z => x+2y+z = 2x+2y => x=z

Hence by moving slowPointer to start of linked list, and making both slowPointer and fastPointer to move one node at a time, they both have same distance to cover .

They will reach at the point where the loop starts in the linked list.

Diagram of proof

笑,眼淚并存 2024-10-04 15:28:00

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

仅当快速指针恰好绕了一次循环时,它才是正确的 - 即循环开始之前的部分比循环的长度短 - 但如果它比算法长,那么它是正确的不起作用;你可能会陷入无限循环。

尝试使用包含 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 -> 1 2 10-> 3 11-> 4 7 -> 5 8 -> 6 9 -> 7 10 --> 8 11 --> 9 7 -> 10 8 -> 11 9 -> 11 7 10 --> 8 11 ->...

The second part, stating that "to find the start of the loop, just move one pointer back to the start of the list then iterate both until they meet" is wrong!

It is correct only if the fast pointer has gone round the loop exactly once - i.e. that the portion before the start of the loop is shorter than the length of the loop - but if it is longer then the algorithm doesn't work; you can get stuck in an infinite loop.

Try it with a linked list with 11 elements, where the 11th points to the 7th:

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

-- loop detected

Move one back to start and increment them:
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 ->...

跨年 2024-10-04 15:28:00
/* 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;
}
/* 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;
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文