面试题:链表环的检测(Floyd判圈算法)

来源:互联网 发布:修改文件权限linux 编辑:程序博客网 时间:2024/05/21 19:34

本文最初发于:http://03071344.lofter.com/post/10871e_29fdd8

PS:本文介绍的,既是Floyd判圈算法,可搜索该关键字查找其它资源

去年听师兄讲了一道链表环的问题,给出的解法是:用两个指针p1、p2,它们初试都指向链表头,然后开始向后移动。

指针p1每次移动一步,p2则每次移动两步。如果在移动过程中p2到达链表末端了,表明该链表无环。如果在移动过程中p1和p2相遇了,那么就表示链表有环。

师兄又讲了如何求环的长度,虽然方法同样简单,但是我不是很能理解。就是上面的算法,我也不能确定结果一定正确。师兄说关键在于p2每次都比p1多走一步。


昨天下午又和师弟聊到了这个话题。终于理解了这个算法,同时师弟还之处,从开始到第一次相遇p1所走的步数(x+y,其中x是在直线上走的步数,y为环上所走步数),是环长度n的整数倍。

下面我想说说为什么这个环检测算法有效。且后面的讨论都是针对确实存在环的链表的情况,因为如果一个链表无环的话,那么p2在移动的过程中一定会遇到空指针,即标识链表结束。

假设我们有一个环如图所示:


其中是链表的起始点,是链表进入环的位置。是p1刚进入环时,p2的位置。

定义相邻两个节点的距离为1,当他们中间间隔1个节点时距离为2。称a在b的前面t步,表示在a不动的情况下,b需要移动t步才能和a重合。并定义环的长度长度为从环上任意一点开始,不断前进回到原点所需的步数,记为n。

当指针p1和p2在直线上时,他们从左至右移动;当他们在环上时,则逆时针移动。其中x表示直线链表起始点和终结点之间的距离,y为p1刚进入环时p2距离p1的距离。

由于每一轮行走p1都只移动一步,而p2可以移动2两步,所以走了t步之后,p2将领先p1t步。而在环上,若p1、p2起始点相同,且在随后的n步之后p2再次与p1相遇,可知p2领先了p1n步,进而可以推知环的长度为n。而且他们一定是在一起出发的地点相遇。

类似的,若p1在  ,p2在 。则经过y步之后,p2将赶上p1,两者相遇。由此,我们已经证明在存在环的链表上,当p1、p2每次移动的步数之差为1时,他们一定相遇。且当他们都在环上同一点出发的情况下,下次相遇之前所走的步数是环的长度。


下面我们证明他们从链表头开始走,一直到的第一次相遇所走的步数(x+y),刚好是圆环的长度n的整数倍。

首先,p1走了x步到达 时,p2也刚好以 为起点在圆环上走了x步到达 (可能已经转了几个圈,也可能还没有);而如果他从 继续往前走y步,就回到了它进入圆环的起点 ;可以推知p2与p1从链表起点开始,第一次相遇在 之后的y步。

由上可知,x+y一定是n的整数倍。而x+y恰好是p1在第一次与p2相遇时所走的步数。证明完成。