双向链表节点必须用指针定义,因为Go中结构体按值传递会导致复制后指针失效;正确定义为type Node struct{Data int; Next, Prev Node}和type DoublyLinkedList struct{Head, Tail Node; Size int}。

为什么双向链表的节点必须用指针定义
Go 语言中,结构体默认按值传递。如果 Node 是普通结构体类型,每次插入、删除或遍历时复制整个节点(含前后指针和数据),不仅浪费内存,更会导致 next 和 prev 指向失效——因为复制后的新节点指针仍指向旧地址,而原节点可能已被回收或移动。
所以必须用指针类型:*Node,确保所有操作都作用于同一块堆内存上的节点实例。
如何正确定义双向链表节点和链表结构
节点需包含数据字段、next 和 prev 两个 *Node 字段;链表结构体通常只保留头尾指针和长度,避免冗余状态。
常见错误是把 next/prev 定义为 Node(非指针),或在链表结构里存了重复的 head/tail 且未同步更新。
type Node struct {
Data int
Next *Node
Prev *Node
}
type DoublyLinkedList struct {
Head *Node
Tail *Node
Size int
}
插入节点时最容易漏掉的双向链接步骤
插入不是单向“连上一个”,而是要同时修正四个指针:新节点的 Next/Prev,以及它左右邻居的对应指针。尤其在头插/尾插时,容易忘记更新 Head 或 Tail 的指向,或漏掉对空链表的特殊处理。
- 头插:新节点
Next = list.Head,若原Head != nil则Head.Prev = newNode;最后list.Head = newNode - 尾插:新节点
Prev = list.Tail,若原Tail != nil则Tail.Next = newNode;最后list.Tail = newNode - 中间插入(如在
target后):设newNode.Next = target.Next,newNode.Prev = target,再更新target.Next.Prev = newNode和target.Next = newNode
遍历和删除时的空指针 panic 风险点
Go 运行时报 panic: runtime error: invalid memory address or nil pointer dereference 很大概率出现在遍历或删除逻辑里——比如没检查 current != nil 就直接访问 current.Next,或删除唯一节点后未将 Head 和 Tail 同时置为 nil。
立即学习“go语言免费学习笔记(深入)”;
- 正向遍历必须写成:
for current := list.Head; current != nil; current = current.Next - 删除节点前,先判断是否为头/尾节点,并分别处理:
if node == list.Head→ 更新list.Head = node.Next;若新Head不为空,还要Head.Prev = nil - 删除后务必手动置空已释放节点的指针字段(非必需但利于调试):
node.Next, node.Prev = nil, nil
Size」和「在多 goroutine 场景下未加锁」——这两个不导致编译失败,但会让行为变得不可预测。










