141. 环形链表

简单 含交互动画

学完这道你应该能: 说清它为什么用「快慢指针」,而不是死记步骤; 合上页面,自己默写出核心代码; 用 30 秒把思路讲清楚。

AI 私教 · 就着本题动画讲
卡住了不用硬扛,按 8 步把这题真正走通。
从读题、试解、找法到手写代码,边问边打勾,适合第一次系统学算法的同学。

题目描述

判断链表中是否存在(某个节点的 next 指回了前面的节点)。

链表 = 3 → 2 → 0 → −4
= −4 的 next 指回 2
输出 = true

思路解析

边走边把节点塞进集合,遇到一个已经存过的就说明有环。能做,但要 O(n) 空间存所有节点。

转折:不用哈希。两个指针都从头出发,fast 每次 2 步、slow 每次 1 步。没有环,fast 一路领先先冲到 null(false);有环,fast 进环后每走一步就把和 slow 的距离缩短 1,绝不会跳过,最终在环里追上。只用 O(1) 空间。

起点 · slow=fast=头:slowfast 都从头 3 出发。注意末尾 −4 的箭头是橙色回边「↺ 回到 2」——这就是环。

第 1 轮 · slow 走 1 步:slow 慢慢来,先走 1 步到 2。fast 还停在 3。

第 1 轮 · fast 走 2 步:fast 一口气走 2 步:3 → 2 → 0。slow≠fast,没相遇,继续。两个指针现在都已进环。

第 1 轮 · 判 slow≠fast,继续:每轮末尾比一次:slow 在 2、fast 在 0,不是同一节点,没相遇,进入下一轮。注意此刻两者在环里只隔 1 个节点了。

第 2 轮 · slow 走 1 步:slow 再走 1 步到 0。此刻它和 fast 都在 0——但这是 slow 刚到、还没轮到判相遇(要等本轮 fast 也走完再比)。

第 2 轮 · fast 沿回边绕回 2:fast 走 2 步:0 → −4 → 沿回边绕回 2。看着像“后退”,其实是绕环一圈的一部分。slow 在 0、fast 在 2,仍未相遇。

第 3 轮 · slow 走 1 步:slow 走 1 步到 −4。注意 slow 和 fast 的距离一直在缩小:从隔 2 个,到隔 1 个。

第 3 轮 · fast 走 2 步 · 相遇!:fast 走 2 步:2 → 0 → −4,正好撞上 slow!slow is fast,相遇,证明链表有环,返回 true。若无环,fast 早就走到 null 了。

Floyd 判圈(龟兔赛跑):进环后 fast 每步把和 slow 的距离缩短 1,绝不会“跳过”,所以一定相遇。找环的入口(142)、找重复数(287)都用它。

参考代码(Python)

Python
1slow = fast = head
2while fast and fast.next:           # 无环时这里会停
3    slow = slow.next                # 慢走 1
4    fast = fast.next.next           # 快走 2
5    if slow is fast: return True    # 相遇 = 有环
6return False                        # fast 到 null = 无环

复杂度分析

  • 时间复杂度:O(n) —— 有环时 fast 最多绕一圈追上
  • 空间复杂度:O(1) —— 两个指针,不用哈希

套路模板

记住骨架:快慢同出发、快2慢1、相遇即有环、fast到null即无环。要找环入口,就在相遇后再用一个指针从头与 slow 同速走,相遇点就是入口。

Python
1slow = fast = head
2while fast and fast.next:
3    slow, fast = slow.next, fast.next.next
4    if slow is fast: return True    # 相遇=有环
5return False

易错点

  • 错误写法:用 slow.val == fast.val 判相遇正确写法:用 slow is fast 判同一节点(不同节点可能值相同,要比身份不比值)
  • 错误写法:循环条件漏判 fast.next正确写法:while fast and fast.next(fast 跨两步前要确保中间节点存在)

以上文字与上方动画完全同一思路:先看动画建立直觉 → 读文字巩固 → 关掉页面自己默写一遍代码,能写出来才算真的掌握

下一题 →2. 两数相加 ← 返回题库