使用 Hare and Tortoise 方法在链表中检测循环
Posted
技术标签:
【中文标题】使用 Hare and Tortoise 方法在链表中检测循环【英文标题】:Cycle detection in linked list with the Hare and Tortoise approach 【发布时间】:2011-09-22 21:32:06 【问题描述】:我知道,为了检测链表中的循环,我可以使用 Hare and Tortoise 方法,该方法包含 2 个指针(慢指针和快指针)。但是,在阅读wiki和其他资源后,我不明白为什么保证两个指针会在O(n)时间复杂度内相遇。
【问题讨论】:
您是在寻找正式的数学证明,还是只是非正式的解释? 正式(但易于理解)证明。检查顶部的第二个答案。 ***.com/questions/2936213/… 【参考方案1】:这是一个非正式证明的尝试。
循环的形状无关紧要。它可以有一个长尾巴,和一个朝向末端的循环,或者只是一个从头到尾没有尾巴的循环。不管周期的形状如何,有一点很清楚 - 乌龟指针无法赶上兔子指针。如果两者要相遇,兔子指针必须从后面追上龟指针。
确定后,考虑两种可能性:
-
野兔指针比乌龟指针落后一步。
野兔指针比乌龟指针落后两步。
所有更大的距离最终都会减少到一两个。
假设乌龟指针总是先移动(也可以反过来),那么在第一种情况下,乌龟指针向前移动一步。现在它们之间的距离为 2。当野兔指针现在走 2 步时,它们将落在同一个节点上。为了更容易理解,这里说明了相同的内容。过多的文字可能会妨碍您。
♛ = 野兔 ♙ = 乌龟 X = 两者相遇 ..♛♙... #1 - 最初,兔子比乌龟落后一步。 ..♛.♙.. #2 - 现在乌龟迈出了一步。现在野兔落后了两步。 ....X.. #3 - 接下来,兔子走了两步,它们相遇了!现在让我们考虑第二种情况,它们之间的距离为 2。慢速指针向前移动一步,它们之间的距离变为 3。接下来,快速指针向前移动两步,它们之间的距离减小到 1,即与第一种情况相同,在第一种情况下,我们已经证明它们将再一步相遇。同样,为了便于理解,还进行了图示。
.♛.♙... #1 - 最初,兔子比乌龟落后两步。 .♛..♙.. #2 - 现在乌龟迈了一步,它们之间的距离变成了三步。 ...♛♙.. #3 - 接下来,兔子采取两个步骤,这与之前的情况相同。现在,至于为什么这个算法保证在 O(n) 内,使用我们已经确定的兔子将在乌龟前进之前遇到乌龟。这意味着一旦两者都在循环内,在乌龟完成另一轮之前,它将遇到兔子,因为兔子的移动速度是兔子的两倍。在最坏的情况下,循环将是一个有 n 个节点的圆。乌龟只能在 n 步内完成一轮,而兔子可以在这段时间内完成两轮。即使兔子在第一轮错过了乌龟(它会),它肯定会在第二轮赶上乌龟。
【讨论】:
知道了,谢谢!所以只是想确保我完全理解它 - 一旦慢速指针进入循环(快速指针显然已经在其中),保证快速指针第一次尝试绕过慢速指针时,它们实际上会见面。 是的,绝对的,而且由于快速指针在n
移动中遍历循环两次(考虑到循环的长度为n
),它们保证在O(n)
中相遇。还要证明为什么我们不能有比O(n)
的下限,考虑整个列表循环并看起来像一个圆圈的最坏情况。两者都从节点 0 开始。当快指针完成一个循环时,慢指针已经在列表(n/2)
步骤的一半处。在另一个(n/2)
步骤中,快的将完成另一次迭代,而慢的将完成第一次迭代。【参考方案2】:
让迭代器A
和B
分别一个接一个地遍历列表。康迪尔存在一个循环。然后在A
进入循环的那一刻,B
已经在其中的某个地方。如果循环的长度是K
,那么B
将在]K/2[
移动中围绕它运行,所以最多在2*]K/2[
迭代中我们会遇到B
出现在远处A
后面的情况1: B->A
或 2: B->.->A
,这是 B
'th 回合。在此之后,显然,他们将在1
或2
移动中相遇。因此,如果循环存在并从某个位置 P
开始,那么我们最多会进行 2*P + 2*]K/2[ + 2 = O(N)
迭代。
【讨论】:
【参考方案3】://if you just want to check if there is a loop
loop = false;
item = head-of-list;
while (item != nil)
if (item.next < 0)
loop = true;
break;
else
actual = item;
item = item.next;
actual.next = actual.next * -1;
return loop;
【讨论】:
【参考方案4】:这是龟兔算法的while循环:
while tortoise:
hare = hare.next
tortoise = tortoise.next
# if not hare, states that i have a single node.
# hare.next means that we have a tail value. So we do not have a cycle
if (not hare) or (not hare.next):
return False
else:
hare = hare.next
if tortoise == hare:
return True
虽然兔子向前移动了2步,这意味着它有可能在循环中一遍又一遍地循环,一遍又一遍地接触多个节点,从技术上讲,这一切都发生在一个while
循环中.
【讨论】:
以上是关于使用 Hare and Tortoise 方法在链表中检测循环的主要内容,如果未能解决你的问题,请参考以下文章