Algorithm 为什么在链表中查找循环时将指针增加2,为什么不增加3,4,5?

Algorithm 为什么在链表中查找循环时将指针增加2,为什么不增加3,4,5?,algorithm,data-structures,linked-list,cycle,floyd-cycle-finding,Algorithm,Data Structures,Linked List,Cycle,Floyd Cycle Finding,我已经看了关于在链表中查找循环的算法。我读过解决方案,在很多地方提到,我们必须采取两个指针。一个指针(较慢/乌龟)增加一个,另一个指针(较快/兔子)增加2个。当它们相等时,我们找到循环,如果更快的指针达到null,则链表中没有循环 现在我的问题是为什么我们把更快的指针增加2。为什么不做点别的?增加2是必要的,或者我们可以增加X来得到结果。如果我们将更快的指针增加2,是否有必要找到一个循环,或者可能需要增加3、5或x。没有理由需要使用数字2。任何步长的选择都会起作用(当然,除了一个) 看看为什么这

我已经看了关于在链表中查找循环的算法。我读过解决方案,在很多地方提到,我们必须采取两个指针。一个指针(较慢/乌龟)增加一个,另一个指针(较快/兔子)增加2个。当它们相等时,我们找到循环,如果更快的指针达到null,则链表中没有循环


现在我的问题是为什么我们把更快的指针增加2。为什么不做点别的?增加2是必要的,或者我们可以增加X来得到结果。如果我们将更快的指针增加2,是否有必要找到一个循环,或者可能需要增加3、5或x。没有理由需要使用数字2。任何步长的选择都会起作用(当然,除了一个)

看看为什么这样做,让我们来看看为什么弗洛依德的算法首先工作。想法是考虑序列x0,x1,x2,…,xn。。。如果您从列表的开头开始,然后继续向下走,直到到达末尾,您将访问的链接列表的元素。如果列表不包含循环,则所有这些值都是不同的。如果它确实包含一个循环,那么这个序列将无休止地重复

下面是使Floyd算法起作用的定理:

链表包含一个循环,当且仅当存在正整数j时,对于任何正整数k,xj=xjk

让我们去证明这一点;没那么难。对于“if”情况,如果存在这样的j,则选择k=2。对于一些正的j,xj=x2j和≠ 2j,因此该列表包含一个循环

对于另一个方向,假设列表包含从位置s开始的长度为l的循环。设j是l大于s的最小倍数。然后对于任何k,如果我们考虑Xj和XJK,因为j是循环长度的倍数,我们可以认为XJK是从列表中的位置j开始形成的元素,然后取j步K-1次。但是每一次你采取j步,你都会回到你在列表中开始的地方,因为j是循环长度的倍数。因此,xj=xjk

这个证明向您保证,如果您在每次迭代中采取任何固定数量的步骤,您确实会碰到慢指针。更准确地说,如果在每次迭代中采取k个步骤,那么最终将找到点xj和xkj,并检测循环。直观地说,人们倾向于选择k=2来最小化运行时间,因为每次迭代的步骤数最少

我们可以更正式地分析运行时,如下所示。如果列表不包含循环,则快速指针将在n个步骤后到达列表末尾,持续O(n)个时间,其中n是列表中的元素数。否则,两个指针将在慢速指针执行j步后相遇。记住,j是l大于s的最小倍数。如果是≤ l、 那么j=l;否则,如果s>l,则j最多为2s,因此j的值为O(s+l)。因为l和s不能大于列表中的元素数,这意味着j=O(n)。但是,在慢速指针执行j步之后,对于慢速指针执行的j步,快速指针将执行k步,因此它将执行O(kj)步。由于j=O(n),网络运行时最多为O(nk)。请注意,这意味着我们使用快速指针执行的步骤越多,算法完成所需的时间就越长(尽管只是成比例的)。因此,选择k=2可以最小化算法的总体运行时间


希望这有帮助

考虑一个大小为L的循环,这意味着第k个元素处的循环是:xk->xk+1->……->xk+L-1->xk。假设一个指针以r1=1的速率运行,另一个指针以r2的速率运行。当第一个指针到达xk时,第二个指针将已经在某个元素xk+s的循环中,其中0如果链表有一个循环,那么增量为2的快速指针将比增量为3或4或更多更好,因为它确保一旦我们进入循环,指针肯定会发生碰撞,并且不会发生任何冲突超车

例如,如果我们取增量3,在循环内部,让我们假设

fast pointer --> i  
slow         --> i+1 
the next iteration
fast pointer --> i+3  
slow         --> i+2
然而,当增量为2时,这种情况永远不会发生

此外,如果您真的很不走运,那么您可能最终会遇到循环长度为
L
且快速指针的增量为
L+1
的情况。然后,您将被无限地卡住,因为移动快指针和慢指针的差值始终为
L


我希望我能说清楚。

让我们假设不包含循环的列表的长度为
s
,循环的长度为
t
,并且
快指针速度与
慢指针速度的比率为
k

让两个指针在距离循环起点
j
的距离处相交

因此,慢速指针移动的距离=
s+j
。快速指针移动的距离=
s+j+m*t
(其中
m
是快速指针完成循环的次数)。但是,快速指针也会移动一段距离
k*(s+j)
k
乘以慢速指针的距离)

因此,我们得到
k*(s+j)=s+j+m*t

s+j=(m/k-1)t

因此,从上面的等式中,慢速指针移动的长度是循环长度的整数倍

为了获得最大的效率,
(m/k-1)=1
(慢速指针不应该在循环中移动超过一次。)

因此,
m=k-1=>k=m+1

由于
m
是快速指针完成循环的次数,
m>=1
。 为了获得最大的效率,
m=1

特里福