快慢指针for循环需确保fast != nil && fast.Next != nil再更新fast = fast.Next.Next和slow = slow.Next;初始均从head出发;不加Visited字段,避免破坏结构和超空间复杂度。

快慢指针的 for 循环怎么写才不会死循环
核心是:快指针每次走两步,慢指针每次走一步,两者从同一节点出发;只要快指针没走到 nil,就继续循环。一旦快慢指针相遇,说明有环。
常见错误是把终止条件写成 fast != nil && fast.Next != nil 却在循环体内直接访问 fast.Next.Next,导致 panic。
- 正确写法是先判
fast != nil && fast.Next != nil,再让fast = fast.Next.Next - 慢指针必须同步更新:
slow = slow.Next,不能漏 - 初始时两个指针都从
head出发(不是head.Next),否则单节点无环也会误判
ListNode 结构体定义里要不要加 Visited 字段
不要加。加标记字段属于「破坏性检测」,会污染原始数据结构,且不满足 O(1) 空间复杂度要求。
快慢指针法的优势就是只用两个指针变量,不改链表、不额外建 map、不递归——这是面试和生产中真正被要求的解法。
初始化并管理阿里云SDK Go 客户端,涵盖 sync.Once 单例、goroutine 安全、endpoint 与 region 配置、VPC 端点及调试。
立即学习“go语言免费学习笔记(深入)”;
- 用 map 记录地址(
map[*ListNode]bool)虽直观,但空间 O(n),且 Go 中指针做 map key 要小心 GC 期间地址变化(实际稳定,但属冗余) - 递归栈深度可能爆栈,链表长了直接 panic: stack overflow
- 所有「打标」方案都不如快慢指针简洁可靠
为什么快指针走 2 步、慢指针走 1 步,而不是 3 和 1 或 2 和 2
因为只有「速度差为 1」才能保证在环内必然相遇。如果都走 2 步,相对静止,永远追不上;如果快指针走 3 步,可能跳过慢指针,理论上仍会相遇,但步数不必要地增多,且边界判断更复杂。
数学上:设环长为 L,慢指针入环时快指针已在环内某处,二者距离为 k(0 ≤ k
- 快 2 / 慢 1 → 相对速度 = 1 → 必相遇
- 快 3 / 慢 1 → 相对速度 = 2 → 若 k 是偶数能相遇,奇数则可能“擦肩”,需更多轮,但最终仍会相遇(因 L 和 2 不一定互质,不过实现上没必要引入不确定性)
- 实际就用 2 和 1,最简、最稳、最通用
遇到 panic: runtime error: invalid memory address or nil pointer dereference 怎么快速定位
90% 是在访问 fast.Next 或 fast.Next.Next 前没检查 fast 是否为 nil,或者漏判 fast.Next == nil。
典型错误代码:for fast.Next != nil { fast = fast.Next.Next } —— 这里没检查 fast 本身是否为 nil,一进来就 panic。
- 安全写法只有一条路径:
for fast != nil && fast.Next != nil - 进入循环后,
fast.Next和fast.Next.Next都可安全访问 - 空链表(
head == nil)要提前 return false,否则fast = head后立刻 panic

















