链表求环问题及其背后的原理

对于链表这一常见的数据结构,相信大多数朋友早已烂熟于心。虽然其基本的操作(插入、删除、遍历)非常容易实现,但对于一些高级操作,其实现就要稍微费一些周折了。比如今天的主题--链表求环问题:

给定一个链表,判断其是以NULL结尾,还是形成一个环。如果链表中存在环,如何求出环的起始节点。

判断链表中是否存在环的比较好的方法是“快慢指针”法(由Floyd提出,故该方法又称为Floyd环判定算法)。主要思想是使用两个在链表中以不同速度移动的指针--“快”指针和“慢”指针。两个指针从表头开始移动,快指针每次移动2个节点,慢指针每次移动一个节点,这样如果链表中存在环,则它们就会相遇。该方法比较容易理解,下图展示了Floyd算法的分步执行过程,从中可以发现,两个指针将在环中可能并非起点的某一点相遇:

Capture

如上面最后一个图中所示,设从链表头节点到环开始节点之间的长度为M,环本身的长度为L,快慢指针相遇时慢指针在环内移动的距离为x,则下面的等式成立:

x mod L = (2(M+x)-M) mod L = (M+2x) mod L  

可令 x = k1L +d, M+2x = k2L +d,可求得 m = (k2 - 2k1)L -d。由于k1>=0且k2-2k1>0,于是可取k1 =0, k2=1,于是

M = L -d  

不失一般性,由于k2-2k1也是一个正整数,记为k3,于是有

扫描二维码关注公众号,回复: 2893226 查看本文章

M =k3 L -d  

基于这个式子表达出的M、L和d之间的关系,于是我们可以如下求出环的起始节点S:

当快慢指针相遇后,让慢指针重新指向链表的头节点,快指针保持在相遇的位置不动,然后以步长1同时移动快、慢指针,直至它们相遇。不难得出,这时相遇的节点就是环的起始节点。而此时快指针已经在环内绕了k3圈(k3>=0),k3的大小取决于链表的“直链”部分与“环状”部分长度的比值。


以上就是链表求环问题背后的原理,相应的代码实现请参考本人的Github项目DataStructure下LinkedList中FindBeginOfLoop()方法,谢谢惠顾!

猜你喜欢

转载自blog.51cto.com/10827670/2164100
今日推荐