核心思想
两个指针同向移动,慢指针每次走 1 步,快指针每次走 2 步。利用速度差来判断链表结构或找到特殊位置,无需额外空间。
适用场景
判断链表是否为环
slow走1步,fast走2步,fast 追上 slow 说明有环,fast 到达 null 说明无环。
为什么一定会相遇?进入环后,每轮 fast 比 slow 多走 1 步,相对距离每轮减 1,必然归零。
相关题目:Leetcode 141.环形链表
class Solution {
public:
bool hasCycle(ListNode *head) {
if (!head) return false;
ListNode* sl = head, *fa = head->next;
while (sl != fa) {
if (fa == nullptr || fa->next == nullptr) return false;
sl = sl->next;
fa = fa->next->next;
}
return true;
}
};判断环形链表的入口
使用Floyd 判圈算法,当我们用快慢指针使它们相遇后,再将链表的头节点和相遇点同时向前移动,当它们相遇(注意这里说的它们是链表头节点和快慢指针的相遇点,不是第一次快慢指针)时,那个点就是环形链表的入口。
数学证明(记住结论即可): 设链表头到入口距离 =
a,入口到相遇点 =b,环长 =c。
相遇时:2(a+b) = a+b+c→a = c−b
即:从 head 到入口的距离 = 从相遇点继续走到入口的距离。
所以把 slow 重置到 head,fast 留在相遇点,同速走,再次相遇即入口。
相关题目:Leetcode 142
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
ListNode* sl = head, *fa = head;
while (true) {
if (fa == nullptr || fa->next == nullptr) {
return nullptr;
}
sl = sl->next;
fa = fa->next->next;
if (sl == fa) {
break;
}
}
sl = head;
while (sl != fa) {
sl = sl->next;
fa = fa->next;
}
return sl;
}
};寻找链表的中间点
同样是快慢指针,慢指针走一步,快指针走两步。 当快指针走完链表时,慢指针正好停在了链表的中间位置。
相关题目:Leetcode 876
class Solution {
public:
ListNode* middleNode(ListNode* head) {
ListNode* sl = head, *fa = head;
while (fa && fa->next) {
sl = sl->next;
fa = fa->next->next;
}
return sl;
}
};注意对于偶数长度的链表,中间点有两个,比如长度为6的链表第3,4都是中间点。上面的代码选择的是后面那个,如果要返回前面那个改为
fa = head->next即可,但要注意空指针的情况。