判断链表是否包含环
LeetCode 141, 142
这个技巧也在前文 双指针技巧汇总 写过,如果看过的读者可以跳过。
判断链表是否包含环属于经典问题了,解决方案也是用快慢指针:
每当慢指针 slow
前进一步,快指针 fast
就前进两步。
如果 fast
最终遇到空指针,说明链表中没有环;如果 fast
最终和 slow
相遇,那肯定是 fast
超过了 slow
一圈,说明链表中含有环。
只需要把寻找链表中点的代码稍加修改就行了:
当然,这个问题还有进阶版:如果链表中含有环,如何计算这个环的起点?
这里简单提一下解法:
可以看到,当快慢指针相遇时,让其中任一个指针指向头节点,然后让它俩以相同速度前进,再次相遇时所在的节点位置就是环开始的位置。
前文 双指针技巧汇总 详细解释了其中的原理,这里简单说一下。
我们假设快慢指针相遇时,慢指针 slow
走了 k
步,那么快指针 fast
一定走了 2k
步:
fast
一定比 slow
多走了 k
步,这多走的 k
步其实就是 fast
指针在环里转圈圈,所以 k
的值就是环长度的「整数倍」。
假设相遇点距环的起点的距离为 m
,那么结合上图的 slow
指针,环的起点距头结点 head
的距离为 k - m
,也就是说如果从 head
前进 k - m
步就能到达环起点。
巧的是,如果从相遇点继续前进 k - m
步,也恰好到达环起点。因为结合上图的 fast
指针,从相遇点开始走k步可以转回到相遇点,那走 k - m
步肯定就走到环起点了:
所以,只要我们把快慢指针中的任一个重新指向 head
,然后两个指针同速前进,k - m
步后一定会相遇,相遇之处就是环的起点了。
可用loop解决的问题(注意这里fast,slow都从head开始)
1.环的长度是多少?
2.如何将有环的链表变成单链表?(解除环)
3.如何判断两个单链表是否有交点?如何找到第一个交点?
(Y为环的第一个点,Z是fast,slow第一次相遇的点)
Solution for 1.环的长度
1>第一相遇后,让fast,slow继续走,记录下下次相遇时环走了几次。
fast第二次到Z时,fast走了一圈,slow半圈。
fast第三次到Z时,fast走了两圈,slow一圈,正好有在Z相遇。
2>第一次相遇后,fast不走,让slow走,下次相遇时环的长度。
3>(Easiest)第一次相遇时slow走了a+b, fast走了a+b+c, 因为fast速度是slow的两倍,所以2(a+b)=a+b+c+b
所以a=c
环的长度L=a+b=b+c, 到第一次相遇,slow经过的长度就是环的长度L.
Solution for 2: 解除环变成单链表
将C段中Y之前的点与Y切断即可。
Solution for 3: 判断两个单链表是否有交点,有的话第一个交点在哪里 (LeetCode 160)
Version 1:
step 1:先判断两个链表是否有环,若一个有环一个没环 ->不相交
step 2:都没环,判断其尾部是不是相等
step 3:都有环,判断一个链表上的Z点是否在另一个链表上。
Version 2(recommend):
让L1 tail.next= L2.head. 判断是否有环,有环就有交点。
找到第一个相交的点 (LeetCode 160):
求出 L1 L2的长度,若L1<L2,用fast slow分别从头部开始走,L2先走(L2-L1)步,然后在一起走,直到相遇。
Solution for LinkedList Cycle II (LeetCode 142)--找到Y点
因为a=c,第一次相遇在Z之后让两个指针fast留在Z, slow变回head开始走,每次走一步,正好在Y相遇。
Last updated