快慢指针能检测环是因为相对速度为1且环长有限,必在环内相遇;第一次相遇后重置慢指针至头节点并同速前进,再次相遇点即为环入口。

为什么快慢指针能检测环?
因为环的存在会让两个以不同速度移动的指针最终相遇。慢指针每次走 1 步,快指针每次走 2 步;如果链表无环,快指针会先走到 None;如果有环,快指针迟早会“套圈”追上慢指针——这不是概率问题,是数学必然:相对速度为 1,环长有限,总能在环内某点相遇。
- 相遇点不一定是环入口,只是证明环存在
- 快指针不能走 3 步或更多:虽然仍可能相遇,但会增加步数不确定性,且无法直接推导入口位置
- 如果快指针走 2 步而慢指针走 1 步,二者距离每次缩小 1,保证最坏情况也能在环长步数内相遇
如何用两次相遇定位环入口?
第一次相遇确认环存在后,把慢指针重置到头节点,快慢指针都改为每次走 1 步,再次相遇的位置就是环入口。这是由链表结构决定的:设头到入口距离为 a,入口到第一次相遇点距离为 b,剩余环长为 c,则快指针路程 = 2(a + b),慢指针 = a + b,差值必为环长整数倍 → 推出 a = c + k(b + c),所以从头和从相遇点各走 a 步必然同时抵达入口。
- 重置后两个指针必须都走 1 步/次,否则等式不成立
- 不需要知道环长或任意长度,纯靠步进同步
- 示例:
def detect_cycle_entry(head): slow = fast = head while fast and fast.next: slow = slow.next fast = fast.next.next if slow == fast: break else: return None # 无环 slow = head while slow != fast: slow = slow.next fast = fast.next return slow # 入口节点
为什么 fast 判空要写成 fast and fast.next?
因为快指针每次跳 2 步,必须确保当前节点和下一个节点都不为 None 才能安全访问 fast.next.next。只判 fast 会漏掉 fast.next is None 的情况,导致访问 None.next 报 AttributeError: 'NoneType' object has no attribute 'next'。
- 错误写法:
while fast:或while fast.next: - 正确顺序不能颠倒:
fast and fast.next是短路求值,先判fast再判fast.next,避免 AttributeError - 如果链表只有一个节点,
fast.next为None,循环直接退出,符合预期
实际调试时最容易忽略的边界
环入口可能是头节点本身(即链表自环),这时第一次相遇发生在头节点,重置慢指针后两指针初始就相等,直接返回 head —— 这种情况容易被手动画图忽略,但代码天然支持。
- 空链表或单节点无环链表必须返回
None,不能假设一定有环 - 节点比较必须用
is(地址比较),不能用==(值比较),否则值重复时误判 - 如果题目要求只检测是否存在环(不要入口),只需完成第一次相遇判断即可,不必重置指针
入口计算依赖的是指针对象的内存地址一致性,不是值;一旦混用 ==,整个逻辑就失效了。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











