go检测环形链表必须用快慢指针,循环条件需同时检查fast != nil && fast.next != nil,否则fast.next.next会panic;空链表或单节点链表须前置返回false;slow == fast判定不可放在循环开头以防误判;禁用map方案以保证o(1)空间复杂度。

Go 里检测环形链表,必须用快慢指针,且循环条件缺一不可:fast != nil && fast.Next != nil。漏掉任一检查,fast.Next.Next 就会 panic。
为什么不能只判 fast != nil
因为 fast.Next.Next 要求 fast.Next 非 nil;如果链表在倒数第二个节点就结束(即 fast 指向倒数第二个,fast.Next 是最后一个节点,fast.Next.Next 就是 nil),此时仅判 fast != nil 不足以阻止解引用 nil。
-
fast == nil→ 直接退出,安全 -
fast != nil && fast.Next == nil→fast.Next.Next会 panic,必须提前拦住 - 正确顺序:先确保
fast != nil && fast.Next != nil,再执行fast = fast.Next.Next
hasCycle 函数的边界初始化必须严格
空链表和单节点链表都不能进循环,否则 slow.Next 或 fast.Next.Next 会越界。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
-
head == nil || head.Next == nil必须前置返回false - 不能把
slow == fast判定写在循环开头——初始时两者都等于head,会直接误判为有环 - 慢指针无需额外判空:只要循环条件保证了
fast可走两步,slow走一步就一定安全(因为slow总比fast落后)
别用 map 记地址来替代快慢指针
虽然 map[*ListNode]bool 能工作,但它带来 O(n) 空间开销和哈希计算,而快慢指针是真正 O(1) 空间、无分配、零 GC 压力的解法。
- 高频调用或嵌入式场景下,map 方案可能触发频繁小对象分配,拖慢整体性能
- Go 的结构体指针相等性比较(
slow == fast)就是地址比较,完全可靠,无需重写Equal方法 - 若真要调试节点是否重复访问,可用
pprof.Lookup("heap").WriteTo抓内存 profile,而不是靠运行时 map
测试时别用结构体值比较验证环
写单元测试时,assert.Equal(t, nodeA, nodeB) 比的是字段值,不是链表拓扑关系;环的存在与否体现在指针路径上,而非字段内容。
- 正确断言方式:用
require.NotNil(t, slow)+require.Equal(t, slow, fast)(地址相等) - 构造带环链表时,必须显式让某个
Next指向已存在节点,比如tail.Next = head或tail.Next = middle - 避免用
fmt.Printf("%+v")打印结构体来“看环”——它不会展开指针循环,只会显示&{...}并卡住或截断
最容易被忽略的一点:环检测本身不解决内存泄漏。只要环中任一节点被全局 map、闭包变量或 goroutine 栈持有,整个环就永远不可达回收——runtime.GC() 不会帮你断环,得从根引用入手排查。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










