206.反转链表
1.题目介绍
题目地址(206. 反转链表 - 力扣(LeetCode))
https://leetcode.cn/problems/reverse-linked-list/
题目描述
给你单链表的头节点 head
,请你反转链表,并返回反转后的链表。
示例 1:
输入:head = [1,2,3,4,5] 输出:[5,4,3,2,1]
示例 2:
输入:head = [1,2] 输出:[2,1]
示例 3:
输入:head = [] 输出:[]
提示:
- 链表中节点的数目范围是
[0, 5000]
-5000 <= Node.val <= 5000
进阶:链表可以选用迭代或递归方式完成反转。你能否用两种方法解决这道题?
作者:力扣官方题解
链接:https://leetcode.cn/problems/reverse-linked-list/solutions/551596/fan-zhuan-lian-biao-by-leetcode-solution-d1k2/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
代码
//
// Created by trmbh on 2023-10-28.
//
#include <iostream>
struct ListNode {
int val;
ListNode *next;
ListNode() : val(0), next(nullptr) {}
ListNode(int x) : val(x), next(nullptr) {}
ListNode(int x, ListNode *next) : val(x), next(next) {}
};
class Solution {
public:
ListNode* reverseList(ListNode* head) {
ListNode *dummy = new ListNode(0); //使用虚拟头结点 辅助存储下一节点
ListNode *pre = nullptr; //存储前一节点
ListNode *curr = head; //存储当前节点
while (curr){
dummy -> next = curr -> next; //记住下一个节点
curr -> next = pre; //修改当前节点指向方向
pre = curr; //当前节点变为下次循环的前一个节点
curr = dummy -> next; // 更改当前节点位置
}
return pre; // 此时当前节点已经指向nullptr,前一节点pre即为所需
}
};
int main(){
ListNode l1(5);
ListNode l2(4, &l1);
ListNode l3(3, &l2);
ListNode l4(2, &l3);
ListNode head(1, &l4);
Solution solution;
ListNode *h = solution.reverseList(&head);
auto p = h;
while (p){
std::cout << p->val << ' ' ;
p = p->next;
}
}
复杂度分析
时间复杂度:O(n),其中 n 是链表的长度。需要遍历链表一次。
空间复杂度:O(1)。
2.2 递归
思路
递归有个特性就是他会一直递归到最里层,然后最里层进行操作,并逐级向上返回。
所以这里我们就有一个想法:能否使用递归的方法,使链表先递归到最里层(最后一个节点),然后再从最里层开始向上逐级进行链表反转?
递归版本关键在于反向工作。假设链表的其余部分已经被反转,现在应该如何反转它前面的部分?
$\text{假设链表为:}\quad n_1\rightarrow\ldots\rightarrow n_{k-1}\rightarrow n_k\rightarrow n_{k+1}\rightarrow\ldots\rightarrow n_m\rightarrow\varnothing $
\(\text{若从节点 }n_{k+1}\text{ 到 }n_m\text{ 已经被反转,而我们正处于 }n_k\text{。}\)
\(n_1\rightarrow\ldots\rightarrow n_{k-1}\rightarrow n_k\rightarrow n_{k+1}\leftarrow\ldots\leftarrow n_m\)
\(\text{我们希望 }n_{k+1}\text{ 的下一个节点指向 }n_k.\)
\(\text{所以,} n_k.next.next=n_k\text{。}\)
需要注意的是 \(n_1\) 的下一个节点必须指向 \(\varnothing\)。如果忽略了这一点,链表中可能会产生环。
具体示例如下图:
代码
这里if (!head || !head->next)
!head是防止空链表的情况,!head->next是使得递归到最后一个节点及时返回。
class Solution {
public:
ListNode* reverseList(ListNode* head) {
if(!head || !head->next) return head; // 防止空链表 和 递归返回条件
ListNode *newNode = reverseList(head->next);
head->next->next = head; // 反转链表
head->next = nullptr; // 断开原来的连接,防止出现环
return newNode;
}
};
作者:力扣官方题解
链接:https://leetcode.cn/problems/reverse-linked-list/solutions/551596/fan-zhuan-lian-biao-by-leetcode-solution-d1k2/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
复杂度分析
时间复杂度:O(n),其中 n 是链表的长度。需要对链表的每个节点进行反转操作。
空间复杂度:O(n),其中 n 是链表的长度。空间复杂度主要取决于递归调用的栈空间,最多为 n 层。
2.3 用栈消除递归
思路
用栈保持前置节点信息,然后从尾结点开始翻转。
代码
class Solution {
public:
ListNode* reverseList(ListNode* head) {
stack<ListNode*> stk;
ListNode *curr = head, *temp = nullptr;
if (!head) return head; // 防止空链表
// 将除最后一个节点的所有指针入栈
while (curr->next){
stk.push(curr);
curr = curr->next;
}
head = curr; //保存反转链表头结点
// 链表反转操作
while (!stk.empty()){
temp = stk.top(); // 获取前一个节点
stk.pop(); // 出栈
curr->next = temp; // 反转链表
temp->next = nullptr; // 断开之前链接,防止形成环
curr = temp; // 当前节点向前移动
}
return head;
}
};