C++如何实现链表反转?(迭代与递归对比)

10次阅读

用三个指针因单向链表无前驱,需保存 prev、curr、next;顺序为缓存 next→改 curr→更新 prev→更新 curr;返回 prev 而非 curr。

C++ 如何实现链表反转?(迭代与递归对比)

迭代反转链表:为什么用三个指针而不是两个

因为单向链表没有前驱指针,必须显式保存 prevcurrnext 三者状态,否则一移动 curr 就丢掉后续节点。常见错误是先改 curr->next 再取 next,导致空指针解引用或循环链表。

实操建议:

  • 初始化 prev = nullptrcurr = head,进循环前先缓存 next = curr->next
  • 顺序固定:改指针 → 更新 prev → 更新 curr(不能颠倒)
  • 返回值是 prev,不是 curr(循环结束时 curr 已为 nullptr
ListNode* reverseList(ListNode* head) {ListNode* prev = nullptr;     ListNode* curr = head;     while (curr) {ListNode* next = curr->next;         curr->next = prev;         prev = curr;         curr = next;}     return prev; }

递归反转链表:函数返回值到底是谁

递归版本的返回值始终是原链表尾节点(即新链表头),不是当前层的 head。很多人误以为要返回 reverseList(head->next) 的结果再处理,其实关键在「递归调用后」才改指针——此时 head->next 已指向新链表尾,只需让它的 next 指回 head,再断开 head->next 即可。

常见错误现象:

立即学习 C++ 免费学习笔记(深入)”;

  • 忘记置空 head->next = nullptr,导致环(尤其测试用例含单节点时必出错)
  • 在递归调用前就修改 head->next,破坏递归前提
  • 边界判断写成 if (!head->next),漏掉 head == nullptr 情况
ListNode* reverseList(ListNode* head) {if (!head || !head->next) return head;     ListNode* newHead = reverseList(head->next);     head->next->next = head;     head->next = nullptr;     return newHead; }

迭代 vs 递归:栈空间和可读性的实际权衡

递归写法简洁,但深度为 n 时必然消耗 O(n) 栈空间;迭代是 O(1) 空间。不过现代编译器对尾递归优化有限,C++ 标准不保证优化,所以别指望递归版能自动转成迭代。

使用场景差异:

  • 链表长度可能上千?选迭代,避免栈溢出(尤其嵌入式或限制栈大小环境)
  • 面试手写代码?递归更易一次写对逻辑,但得主动说明空间代价
  • 需要原地反转且不允许额外容器?两者都满足,但递归隐式用了栈——这不算“额外容器”但算“额外空间”

反转后访问崩溃?检查 head 是否为空和 next 是否已置空

最常被忽略的点:反转操作本身不改变原 head 变量的值,但它的 next 字段已被修改。如果反转后还拿旧 head 当头去遍历,会直接跳到原第二个节点,漏掉新头节点;更糟的是,若忘了在递归版里写 head->next = nullptr,遍历时会无限循环。

调试建议:

  • 反转后立刻打印新头节点的 valnext,确认非空且指向正确
  • 用小样例(如 [1,2])手动走一遍指针变化,比看代码更快定位断点
  • 注意 LeetCode 测试用例包含 head = nullptr,别假设输入总有节点

text=ZqhQzanResources