头指针可能失效是因为交换前两个节点时原head变为第二个节点,必须更新head指向新首节点;否则遍历从错误节点开始。

交换两个相邻节点时,为什么头指针可能失效
因为 head 指针只保存第一个节点地址,若交换的是前两个节点(如 head 和 head->next),原 head 会变成第二个,必须更新 head 指向新首节点。漏掉这步,遍历就从错误节点开始。
实操建议:
- 先判断是否涉及头节点:若
node1 == head或node2 == head,交换后需重赋值head - 不要只改
next,prev同样要同步修正,否则链表断裂或反向遍历出错 - 用临时指针保存原始连接关系,比如交换
a和b(a->next == b)时,先存a->prev、b->next,再重连
swapNodes 函数里最易错的指针四连改顺序
交换相邻节点 a 和 b(a->next == b)需同时调整 4 处指针:a->prev、a->next、b->prev、b->next。顺序错了就会覆盖关键地址,导致内存丢失。
正确顺序(以非头尾为前提):
-
b->prev = a->prev(让 b 接到 a 前面那个节点) -
a->next = b->next(让 a 跳过 b,连到 b 后面) -
a->prev = b(a 的前驱变成 b) -
b->next = a(b 的后继变成 a)
注意:不能先写 a->prev = b,否则 a->prev 原值丢失,无法完成第一步。
交换任意两节点(不相邻)要不要遍历找前驱
要。双向链表虽有 prev,但给定两个节点指针 n1、n2,仍需知道它们各自的前驱和后继才能安全重连——而 n1->prev 和 n2->prev 是已知的,无需额外遍历。
但前提是:你确认这两个节点确实在同一条链上,且不是同一个节点。否则:
- 若
n1 == n2,直接返回,否则后续空指针解引用 - 若节点跨链(比如不同链表或已删除),
prev/next可能野指针,建议加断言assert(n1->next != nullptr || n1 == head)类似校验 - 交换非相邻节点时,中间段链接不变,只需改 8 处指针(每个节点的 prev/next 各 2 个)
用 RAII 管理节点交换中的异常安全问题
纯指针操作不涉及内存分配,但若节点内含资源(如 std::string 成员),交换过程抛异常会导致链表结构损坏。C++ 标准库的 std::list::splice 是异常安全的,但手写需谨慎。
简化做法:
- 把节点数据交换(
std::swap(n1->data, n2->data))而非指针重连,零异常风险,适用于多数场景 - 若真要交换节点位置,确保所有指针修改在无异常路径上完成;避免在修改中途调用可能抛异常的函数
- 调试时可加日志输出各节点的
prev、next地址,比断点更早发现野指针
真正麻烦的从来不是怎么连,而是连完之后没人检查 prev 和 next 是否对称——比如 a->next == b 但 b->prev != a,这种错后期极难定位。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











