不能直接交换节点值,因反转本质是修改指针而非数据;三指针迭代法最稳妥,空间O(1),需正确维护prev、curr、next;递归法要注意栈溢出、终止条件和断链防环。

反转单链表时为什么不能直接交换节点值
因为题目要求的是“链表反转”,本质是修改指针指向关系,而非仅改变数据。若只交换 val,结构未变,遇到含多个字段的自定义节点(如 next、random)或需保持节点内存地址不变的场景(如外部有指针引用该节点),就会出错。
用三指针迭代法反转链表最稳妥
这是面试和工程中最常采用的方式,空间复杂度 O(1),逻辑清晰,不易越界。关键在于维护三个变量:prev、curr、next,每次迭代先保存 curr->next,再让 curr->next = prev,最后推进指针。
常见错误包括:
- 忘记在循环开始前保存
curr->next,导致链表断裂 - 把
prev = curr写成prev = curr->next,跳过节点 - 循环条件写成
curr != nullptr但没处理空链表,实际没问题;但若写成curr->next != nullptr就会漏掉最后一个节点
示例核心逻辑:
立即学习“C++免费学习笔记(深入)”;
ListNode* reverseList(ListNode* head) {
ListNode* prev = nullptr;
ListNode* curr = head;
while (curr != nullptr) {
ListNode* next = curr->next; // 先存下后继
curr->next = prev; // 反转当前指针
prev = curr; // 推进 prev
curr = next; // 推进 curr
}
return prev; // 新头结点
}递归反转链表要注意栈深度和边界返回值
递归写法简洁,但隐含函数调用栈开销。对长度超过几千的链表,可能触发栈溢出(尤其在默认栈空间小的环境,如嵌入式或某些竞赛OJ)。必须确保递归终止条件严格为 head == nullptr || head->next == nullptr,否则空指针解引用会崩溃。
递归的核心在于:先走到尾部,拿到新头结点,再在回溯过程中调整指针。容易出错的是:
- 忘记在递归调用后返回
newHead,导致函数返回随机值 - 误写
head->next->next = head之前未判head->next是否为空(其实终止条件已保证,但加空检查更鲁棒) - 漏设
head->next = nullptr,造成环(比如原链表 1→2→3,反转后若不置空,3→1 且 1→2→3,成环)
简版递归实现:
ListNode* reverseList(ListNode* head) {
if (!head || !head->next) return head;
ListNode* newHead = reverseList(head->next);
head->next->next = head;
head->next = nullptr;
return newHead;
}反转前务必确认链表类型和内存管理方式
标准 LeetCode 风格的 ListNode 是裸指针管理,无 RAII;但若你在项目中用 std::unique_ptr 实现链表,就不能直接用 ->next = prev,而要调用 release() 和 reset() 来转移所有权,否则会触发 double-free 或悬垂指针。
另外,若链表节点由 new 分配,反转后仍需对应 delete,但反转本身不改变析构顺序——这点常被忽略:反转只是改指针,不等于自动释放内存。
真正容易被绕进去的地方是:你以为反转完就可以丢弃原 head 指针,但其实它现在是尾节点,若后续还要遍历到末尾做清理,得从新头出发走到底——别依赖旧变量名判断位置。










