题目
给定单链表的头节点 head,反转链表并返回反转后的头节点。可以使用迭代或递归方式实现。
示例 1:
❯Text
输入:head = [1,2,3,4,5]
输出:[5,4,3,2,1]示例 2:
❯Text
输入:head = [1,2]
输出:[2,1]示例 3:
❯Text
输入:head = []
输出:[]考察内容
- 单链表指针方向修改
- 保存后继节点
- 已反转与未反转区间
- 迭代状态转移
- 递归回溯
解题思路
迭代法
使用三个指针:
prev:已反转部分的头节点;cur:当前准备处理的节点;next:临时保存原链表中cur的后继。
每轮执行四步:
❯Text
next = cur->next 保存后继
cur->next = prev 反转当前指针
prev = cur 已反转部分向前扩展
cur = next 继续处理原后继保存 next 必须发生在修改 cur->next 之前,否则原链表剩余部分的入口会丢失。
循环过程中维护的不变量是:
prev指向已经完成反转的链表;cur指向尚未处理部分的第一个节点;- 两部分包含原链表的全部节点且不重不漏。
当 cur == nullptr 时,所有节点均已反转,prev 就是新的头节点。
- 时间复杂度:
- 空间复杂度:
递归法
递归反转 head->next 开始的子链表,得到新头节点 newHead。回溯时执行:
❯Text
head->next->next = head
head->next = nullptr第一句让原后继指回当前节点,第二句断开原来的正向连接,避免形成环。递归时间复杂度为 ,调用栈空间为 。
代码演示
❯C++
struct ListNode {
int val;
ListNode* next;
explicit ListNode(int value = 0, ListNode* nextNode = nullptr)
: val(value), next(nextNode) {}
};
// 迭代写法:推荐掌握
ListNode* reverseList(ListNode* head) {
ListNode* prev = nullptr;
ListNode* cur = head;
while (cur != nullptr) {
ListNode* next = cur->next;
cur->next = prev;
prev = cur;
cur = next;
}
return prev;
}
// 递归写法
ListNode* reverseListRecursive(ListNode* head) {
if (head == nullptr || head->next == nullptr) {
return head;
}
ListNode* newHead = reverseListRecursive(head->next);
head->next->next = head;
head->next = nullptr;
return newHead;
}易错点
- 修改指针前没有保存后继:执行
cur->next = prev后将无法通过cur找到原来的下一节点。 - 返回
cur:循环结束时cur == nullptr,新头节点是prev。 prev未初始化为空指针:原头节点反转后应成为尾节点,其next必须指向nullptr。- 只交换节点值:题目要求反转链表结构,应修改节点连接关系。
- 递归时忘记断开
head->next:会形成双向连接或环。 - 空链表和单节点链表:迭代写法自然处理;递归写法必须设置基线条件。
总结
反转链表的关键是先保存原后继,再改变当前节点的指向。迭代法把链表分成“已反转”和“待处理”两部分,每轮移动边界;递归法则在回溯阶段反转每一条边。
相关题目
-
- 两两交换链表中的节点
-
- 反转链表 II
-
- 移除链表元素
-
- 回文链表
-
- K 个一组翻转链表