206. 反转链表 - 力扣(LeetCode)
优质题解:【反转链表】:双指针,递归,妖魔化的双指针 - 反转链表 - 力扣(LeetCode)
定义一个函数,输入一个链表的头节点,反转该链表并输出反转后链表的头节点。
输入: 1->2->3->4->5->NULL
输出: 5->4->3->2->1->NULL
数据结构
/**
* Definition for singly-linked list.
* 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) {
}
};
1 --> 2 --> 3 --> 4 --> 5 --> nullptr
nullptr <-- 1 <-- 2 <-- 3 <-- 4 <-- 5
思考过程
1
,因此双指针中的一个肯定指向1
,还有一个指向谁?
1
,另一个只能指向nullptr
,而不是2
。因为1->next = nullptr
1->next=nullptr
执行完毕,1-->2
的键断裂,因此要事先将2保存下来,即next=2
nullptr <-- 1
、2 --> 3 --> 4 --> 5 --> nullptr
curr=1
、pre=nullptr
(pre表示curr的前一个)pre=1
、curr=2
2->next
,再挂接2->next=1
curr->next
,会访问curr的内存,因此要保证curr!=nullptr
curr==nullptr
,那么pre==5
pre
nullptr
。根据代码,确实是返回nullptr
1 -> nullptr
的情况,要返回1
。根据代码,确实是返回1
class Solution {
public:
ListNode* reverseList(ListNode* head) {
ListNode* pre = nullptr;
ListNode* curr = head;
while(curr != nullptr) {
//断链前,要将next保存下来
auto next = curr->next;
//逆序,接链
curr->next = pre;
//下一个状态
pre = curr;
curr = next;
}
return pre;
}
};
递归相对较容易
class Solution {
public:
ListNode* reverseList(ListNode* head) {
//边界条件
if(head == nullptr) return head;
if(head->next == nullptr) return head;
auto old_head = head; //先保留旧的head
head = reverseList(head->next); //翻转后面
//将old_head挂接在最后
auto p = head;
while(p->next != nullptr) { //p有下一个
p = p->next; //继续位移
}
p->next = old_head; //挂old_head
old_head->next = nullptr; //记得old_head的next要置空
return head;
}
};
class Solution {
public:
ListNode* reverseList(ListNode* head) {
//边界条件
if(head == nullptr) return head;
if(head->next == nullptr) return head;
ListNode* newHead = reverseList(head->next); //转剩下的
//head->next其实就是,newHead最后一个节点了
// 无需再遍历一次,直接挂接即可
head->next->next = head;
head->next = nullptr; //记得旧head->next要置空
return newHead;
}
};
【时间复杂度】完整遍历了一次链表,O(n)
【空间复杂度】取决于递归调用的所需的栈空间,即调用的最大深度。同样的,需要完整遍历一次链表,即O(n)