单链表逆置核心是原地修改指针方向,需用prev、curr、next三指针协同推进;常见错误是未暂存next导致断链;递归法易栈溢出且需判空防环;带头结点时只逆置dummy->next部分;必须覆盖空链表和单节点边界测试。

用 next 指针现场翻转,别建新链表
单链表逆置本质是改指针方向,不是复制节点。建新链表或用栈属于绕路,面试官一眼看出没理解原地操作的核心。关键在三个指针协同推进:prev(已逆置部分头)、curr(当前处理节点)、next(暂存下一节点,防止断链)。
常见错误:先写 curr->next = prev 再写 curr = curr->next——这时 curr->next 已被覆盖,curr 直接变 nullptr,循环提前结束。
- 必须先用
next = curr->next保存下一站地址 - 再执行
curr->next = prev - 最后更新
prev = curr和curr = next
Node* reverseList(Node* head) {
Node* prev = nullptr;
Node* curr = head;
while (curr != nullptr) {
Node* next = curr->next; // 先保命
curr->next = prev; // 再翻转
prev = curr; // 前移 prev
curr = next; // 前移 curr
}
return prev; // 新头结点
}递归写法要注意栈溢出和尾调用不优化
递归逆置简洁,但 C++ 不保证尾递归优化,链表一长就爆栈。比如 10 万节点,在多数编译器默认栈大小下必崩。它适合教学演示或极短链表,生产代码慎用。
递归核心是“先走到末尾,再边返回边改指针”。容易错在:返回值没接住、head->next->next 访问前没判空、忘了置 head->next = nullptr(否则成环)。
立即学习“C++免费学习笔记(深入)”;
- 递归终止条件必须是
head == nullptr || head->next == nullptr - 递归调用后,
head->next->next = head前要确认head->next非空 - 每次递归层返回前,设
head->next = nullptr切断旧连接
Node* reverseList(Node* head) {
if (!head || !head->next) return head;
Node* newHead = reverseList(head->next);
head->next->next = head;
head->next = nullptr;
return newHead;
}带头结点的链表,头结点本身不参与逆置
很多教材或面试题里链表带哑节点(dummy),它的 next 才是真实首节点。逆置时只动 dummy->next 开始的部分,dummy 本身位置不动,否则会丢失链表入口。
典型误操作:把 dummy 当作普通节点一起翻转,结果函数返回 dummy 变成尾节点,原链表头彻底找不到。
- 传入参数应为
dummy->next,而非dummy - 逆置完成后,重设
dummy->next = newHead - 不要对
dummy做任何next指针修改
测试时漏掉空链表和单节点边界,运行就崩
空链表(head == nullptr)和单节点(head->next == nullptr)不是“特殊情况”,是必须覆盖的合法输入。漏测会导致 nullptr->next 解引用,触发段错误或未定义行为。
尤其注意:迭代版 while 条件是 curr != nullptr,不是 curr->next != nullptr;递归版终止条件必须双判。
- 单元测试至少覆盖:空链表、单节点、两节点、奇数/偶数长度常规链表
- 检查返回值是否为原尾节点,原头节点的
next是否为nullptr - 用 AddressSanitizer 编译,能快速捕获野指针访问
逆置真正难的不是逻辑,是每一步指针操作都牵一发而动全身。少一个临时变量、多一次空指针解引用、忘清某个 next,结果就是内存乱飞,调试半天才发现是第三行少了个 next = curr->next。










