C++ 怎么实现链表反转 C++ 单链表头插法与递归反转代码【数据结构】

10次阅读

头插法反转单链表通过边遍历边头插实现,时间O(n)、空间O(1);递归法从尾部回溯修改指针,时间O(n)、空间O(n),易溢出。两者均需处理空链表、单节点及野指针等边界。

C++ 怎么实现链表反转 C++ 单链表头插法与递归反转代码【数据结构】

头插法反转单链表:边遍历边重建链表结构

头插法本质是用原链表节点逐个插入到新链表头部,从而自然实现顺序翻转。关键在于维护两个指针:cur 指向当前待处理节点,new_head 指向已反转部分的头节点。

  • 必须先保存 cur->next,否则断开后无法访问后续节点
  • cur 插入新链表时,要让 cur->next = new_head,再更新 new_head = cur
  • 初始时 new_head 应为 nullptr,避免野指针或未定义行为
ListNode* reverseList(ListNode* head) {     ListNode* new_head = nullptr;     ListNode* cur = head;     while (cur != nullptr) {         ListNode* next = cur->next;  // 保存下一个节点         cur->next = new_head;         // 头插         new_head = cur;               // 更新新头         cur = next;     }     return new_head; }

递归反转:从尾节点开始逐层回溯修改指针

递归解法不新建节点,而是靠函数调用“记住”倒数第二个节点,等递归到达尾节点(head->next == nullptr)后,一层层把后继节点的 next 指向前驱。

  • 递归终止条件必须是 head == nullptr || head->next == nullptr,缺一不可,否则空指针解引用
  • 递归返回的是原链表的尾节点,也就是反转后的头节点,全程只返回这一个指针
  • 回溯时执行 head->next->next = head,然后立即置 head->next = nullptr,否则会成环
ListNode* reverseList(ListNode* head) {     if (!head || !head->next) return head;     ListNode* new_head = reverseList(head->next);     head->next->next = head;     head->next = nullptr;     return new_head; }

两种方法的性能与适用场景差异

头插法时间复杂度 O(n),空间 O(1),适合对内存敏感或需迭代控制的场景;递归法时间也是 O(n),但空间 O(n)(栈深度),在链表极长时可能栈溢出。

  • leetcode 等平台测试用例通常较短,递归写法简洁不易错,但生产代码中应优先考虑迭代
  • 若链表节点含非 trivial 析构逻辑(如持有资源),递归更深意味着资源释放延迟更久
  • 头插法可轻松改造成「反转指定区间」或「每 k 个一组反转」,扩展性更强

容易被忽略的边界:空链表、单节点、野指针检查

几乎所有初学者写的反转代码,在 head == nullptrhead->next == nullptr 时逻辑都看似正确,但一旦涉及二级指针操作(比如传入 Listnode** head 做就地修改),漏掉空检查就会直接崩溃。

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

  • 头插法中若忘记 cur = next循环会卡死在第一个节点
  • 递归中若漏写 head->next = nullptr,原头节点仍指向第二个节点,导致反转后链表成环
  • 使用前务必确认 ListNode 定义中 next 成员是否初始化为 nullptr,否则未初始化指针反转后行为不可预测

text=ZqhQzanResources