欢迎来到 s a y − f a l l 的文章 欢迎来到say-fall的文章 欢迎来到say−fall的文章
文章目录
题目:环形链表Ⅱ
一、思路解析
基于 Floyd判圈算法(龟兔赛跑算法) ,核心目标是 不仅检测链表是否有环,还能找到环的入口节点。算法分为「环检测」和「入口定位」两个关键阶段
1. 核心原理铺垫(数学推导)
假设链表结构如下:
- 链表头
head到环入口entry的距离为L(节点数); - 环的长度为
C(环中节点数); - 快慢指针相遇时,慢指针
slow已走S步,快指针fast已走2S步(因快指针每次走2步,慢指针每次走1步,速度比2:1)。
相遇时的关键结论:
- 快指针比慢指针多走的步数
2S - S = S,必然是环长度C的整数倍(快指针在环内绕圈追赶慢指针,多走的路程就是绕环的圈数),即S = k*C(k为正整数); - 慢指针走的路程
S = L + M(M是相遇点meet到环入口entry的距离,0 ≤ M < C); - 结合以上两式:
L + M = k*C→L = k*C - M→L = (k-1)*C + (C - M)。
该式的物理意义:从链表头 head 到入口 entry 的距离 L,等于从相遇点 meet 绕环 (k-1) 圈后,再走到入口 entry 的距离(C - M 是相遇点到入口的剩余距离,绕 k-1 圈后回到相遇点,再走 C-M 步到入口)。
2. 算法执行步骤
阶段1:环检测(快慢指针相遇)
- 初始化:快指针
fast和慢指针slow均指向链表头head; - 循环条件:
fast != NULL && fast->next != NULL(避免快指针访问空指针崩溃); - 指针移动:
slow每次走1步,fast每次走2步; - 相遇判断:若
fast == slow,说明链表有环,记录相遇点meet(meet = slow = fast),进入阶段2;若循环结束未相遇,说明无环,返回NULL。
阶段2:入口定位(双指针同步移动)
- 初始化:新指针
pcur指向链表头head,meet保持在之前的相遇点; - 循环条件:
pcur != meet; - 指针移动:
pcur和meet每次均走1步(速度比1:1); - 终止条件:当
pcur == meet时,两指针同时到达环的入口节点,返回该节点(pcur或meet均可)。
3. 算法特性
- 时间复杂度:O(n),两阶段遍历的总节点数不超过链表总长度(无环时遍历到尾,有环时相遇+入口定位的总步数为 O(n));
- 空间复杂度:O(1),仅使用4个指针(
fast、slow、meet、pcur),无额外空间开销;
二、流程图
三、关键逻辑总结
- 「快慢指针相遇」是链表有环的充要条件,且相遇点必然在环内;
- 「头指针+相遇点指针同步移动」的本质是利用数学推导的距离等价性,无需计算环长和头到入口的距离,直接定位入口;
- 代码复用了环检测的逻辑,在相遇后无缝衔接入口定位,结构简洁且效率最优。
四、代码:
typedef struct ListNode ListNode;
struct ListNode *detectCycle(struct ListNode *head)
{
ListNode* fast = head;
ListNode* slow = head;
while(fast != NULL && fast->next != NULL)
{
fast = fast->next->next;
slow = slow->next;
if(fast == slow)
{
ListNode* meet = slow;
ListNode* pcur = head;
while(pcur != meet)
{
pcur = pcur->next;
meet = meet->next;
}
return meet;
}
}
return NULL;
}
- 本节完…

被折叠的 条评论
为什么被折叠?



