反转链表是链表操作中最基础、最经典的问题之一。看似简单,却能很好地体现对指针、递归、边界处理的理解程度。本文从三种不同思路实现反转链表,并对每一种写法的逻辑与细节做完整梳理。
问题描述
206. 反转链表:给你单链表的头节点 head,请将该链表反转,并返回反转后的链表头节点。
方法一:头插法
头插法的思路非常直观:构建一个新链表,依次将原链表的节点插入到新链表头部,最终得到的链表自然是原链表的逆序。
代码
cpp
class Solution {
public:
ListNode* reverseList(ListNode* head) {
if (head == nullptr || head->next == nullptr)
return head;
ListNode* dummy = new ListNode(0, head);
ListNode* cur = head->next;
ListNode* next = cur->next;
head->next = nullptr;
while (cur != nullptr) {
cur->next = dummy->next;
dummy->next = cur;
cur = next;
if (next) next = next->next;
}
ListNode* res = dummy->next;
delete dummy;
return res;
}
};
特点
- 思路清晰,容易理解与实现
- 借助虚拟头节点,统一处理逻辑
- 时间复杂度 O (n),空间复杂度 O (1)
方法二:三指针迭代法
使用三个指针依次向后遍历,逐个修改节点指向,原地完成反转。这是最常用、最稳健的迭代写法,逻辑紧凑、效率稳定。
代码
cpp
class Solution {
public:
ListNode* reverseList(ListNode* head) {
if (head == nullptr || head->next == nullptr)
return head;
ListNode* prev = head;
ListNode* cur = head->next;
ListNode* next = cur->next;
prev->next = nullptr;
while (cur != nullptr) {
cur->next = prev;
prev = cur;
cur = next;
if (next) next = next->next;
}
return prev;
}
};
特点
- 迭代实现,无栈溢出风险
- 时间复杂度 O (n),空间复杂度 O (1)
- 工程中最推荐的写法
方法三:递归实现
递归思路将问题分解为:**先反转后续子链表,再处理当前节点的指向关系。**代码简洁,适合理解递归思想与链表结构。
- 先递归到链表的最后一个节点,使得newhead记录下反转后的头结点,即原链表的尾结点
- 然后在 "回溯" 的过程中,逐个修改节点的指向,让每个节点的
next指向自己的前一个节点。
代码
cpp
class Solution {
public:
ListNode* reverseList(ListNode* head) {
if (head == nullptr || head->next == nullptr)
return head;
ListNode* newHead = reverseList(head->next);
head->next->next = head; // 使下一节点指向自己
head->next = nullptr; // 使自己指向NULL
return newHead;
}
};
特点
- 代码简洁
- 依靠递归栈实现,空间复杂度 O (n)
- 适合理解分治思想与链表回溯过程
总结
反转链表虽然基础,但包含了链表操作的核心要点:
- 边界判断:空链表、单节点链表直接返回
- 指针移动顺序:避免断链与空指针访问
- 尾节点必须置空,防止出现环
- 迭代与递归各有适用场景,可根据需求选择
扎实掌握这几种写法,能为更复杂的链表题目打下坚实基础。