一、环形链表
题目
给定一个链表,判断链表中是否有环。
如果链表中有某个节点,可以通过连续跟踪 next
指针再次到达,则链表中存在环。 为了表示给定链表中的环,我们使用整数 pos
来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos
是 -1
,则在该链表中没有环。注意:pos
不作为参数进行传递,仅仅是为了标识链表的实际情况。
如果链表中存在环,则返回 true
。 否则,返回 false
。
示例:
输入:head = [3,2,0,-4], pos = 1
输出:true
解释:链表中有一个环,其尾部连接到第二个节点。
思路
使用快慢指针:
- 先初始化快慢指针
fast
、slow
指向头节点 - 快指针每次走两步,慢指针每次走一步
- 若链表成环,最终快慢指针会指向同一个节点
- 若链表不成环,快指针会先走到链表的尾部
代码
function hasCycle(head) {
// 初始化快慢指针
let fast = slow = head;
while(fast !== null && fast.next !== null){// 链表可能有奇数个节点或偶数个节点
// 慢指针每次走一步
slow = slow.next;
// 快指针每次走两步
fast = fast.next.next;
// 如果成环必相遇
if(slow === fast) return true;
}
// 快指针走到链表尾时还没相遇,说明不成环
return false;
};
二、环形链表II
题目
给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null
。
为了表示给定链表中的环,我们使用整数 pos
来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos
是 -1,则在该链表中没有环。注意,pos 仅仅是用于标识环的情况,并不会作为参数传递到函数中。
说明:不允许修改给定的链表。
示例:
输入:head = [3,2,0,-4], pos = 1
输出:返回索引为 1 的链表节点
解释:链表中有一个环,其尾部连接到第二个节点。
思路
由上文双指针方法可知环形链表的相遇点在哪,此时在相遇点处令快指针(或慢指针)指向环形链表的起始节点(头节点),然后令快、慢指针以相同步伐继续走,则快、慢指针再次相遇处即为环的起点。
为什么上述思路就是正确的呢?
👭友情提示:下面的证明会牵涉到一些数学运算,可能有点绕,但是认真阅读、仔细思考后应该都能看懂
快、慢指针从环形链表头节点开始,令快指针每秒走 步,慢指针每秒走 步,快、慢指针走到相遇处时花了 秒。
那么在相遇处时快指针共走了 步,慢指针共走了 步。假设快指针走的环的圈数为 圈,慢指针走的环的圈数为 圈。那么此时,对于快指针就有如下等式
对于慢指针就有如下等式
令 可得 ,即 。由于 ,所以 的值应为 的整数,那么可得 。不妨令 ,此时 ,其中 。
由于在相遇时令快指针指向环形链表的头节点,然后快、慢指针以相同步伐继续走。当快指针走到环的起点时,也就走了 步,又因为 ,所以快指针走了 步。由于快、慢指针是以相同步伐走的,也就是说慢指针从相遇处开始走也走了 步,所以此时我们只要证明慢指针从相遇处开始走了 步后恰好停在环的起点即可证明上述的思路是正确的。而从相遇点以顺时针方向到环的起点距离恰好为 步,那么慢指针走的 步用文字描述的话就可以是:慢指针先从相遇处开始走了 步到环的起点,再在环的起点沿顺时针方向又走了 圈,也就是 步,最终还是回到环的起点与快指针相遇。
代码
// 若一个链表成环,返回环的起始节点
function detectCycle(head) {
// 初始化快、慢指针指向头节点
let slow = head;
let fast = head;
while (fast !== null && fast.next !== null) {
// 慢指针每次前进一步
slow = slow.next;
// 快指针每次前进两步
fast = fast.next.next;
// 如果存在环,快慢指针必然相遇,退出当前while循环
if (slow === fast) break;
}
// 让快指针重新指向头节点
fast = head;
while(true){
// 让快慢指针以相同步伐前进
slow = slow.next;
fast = fast.next;
// 快慢指针相遇的点即为环的起始节点
if(slow === fast) return slow;
}
}
后记
本篇文章(环形链表)的难点之处有两:
- 第一:如何想到利用快慢指针去解这种环形链表的题呢?
- 多刷题呗
- 第二:如何证明快慢指针的思路是正确的呢?
- 需要一定的数学推理能力
该文章主要用于平时的复习巩固和积累,若有书写错误的地方还望各位大佬不吝赐教🙏。