go链表节点必须用指针类型定义,因为struct是值类型,若next为node会导致无限递归定义、编译失败;只有*node有固定大小(如8字节),结构体才可实例化。

Go 语言没有内置链表类型,直接用 []T 切片就能满足大多数场景;真要手写链表,90% 的情况该用 container/list,而不是自己造轮子。
什么时候该自己写 Node 结构体?
只有当你需要:节点类型强约束(比如必须是 int,不能是 interface{})、避免 interface{} 的类型擦除开销、或参与 LeetCode 类算法题时,才值得手动定义 Node。
-
container/list存的是interface{},每次取值都要断言,运行时有额外成本 - 自定义
type Node struct { Data int; Next *Node },编译期就确定内存布局,无反射/断言开销 - 但你要自己处理空指针、循环遍历终止条件、头插/尾插逻辑——比如尾插必须遍历到
curr.Next == nil才能赋值,漏掉判空会 panic
container/list 的遍历为什么容易漏元素?
因为 for e := l.Front(); e != nil; e = e.Next() 这个惯用法看似安全,但一旦你在循环体内调用 l.Remove(e) 或 l.MoveToFront(e),e.Next() 可能已失效——e 被移出链表后,e.Next 指向的是原链表中下一个节点,但该节点可能已被 GC 标记或重链接,导致下一次 e = e.Next() 返回 nil 提前退出。
- 安全做法是先备份
next := e.Next(),再操作e,最后赋值e = next - 或者改用
for e := l.Front(); e != nil;+ 显式推进,避免在循环头更新e -
l.Len()是 O(1),但遍历仍是 O(n);别误以为Len()能帮你跳过遍历
循环链表的遍历终止条件怎么写才不死循环?
普通单链表靠 curr != nil,循环链表必须靠「是否回到起点」来判断,否则无限转圈。常见错误是只检查 curr.Next != head,却忘了初始 curr == head 时就该进循环。
- 正确写法:
if head == nil { return }; curr := head; for { ... if curr.Next == head { break } curr = curr.Next } - 更稳妥的是用计数器 +
Length(),但需注意Length()本身也要遍历一遍——别为了防死循环又引入一次遍历 - 插入新节点时,必须同时更新「前驱的
Next」和「新节点的Next」,漏改任意一个都会破坏环结构
手写链表最易忽略的不是语法,而是边界:空链表传参时是 nil 还是 &Node{},头插后 head 是否及时更新,遍历时修改节点是否影响后续迭代——这些地方一错,调试时 panic 信息往往不指向真正出问题的那行。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











