-
题目:
给定一个单向链表,将其反转并返回反转后的链表头; -
思路:
反转的思路一般有reverse,栈,递归;
对于单链表的特殊性,通常用双指针和递归;
1.双指针:O(n):需遍历一次链表,O(1)
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* reverseList(ListNode* head) {
if (!head) return nullptr;
ListNode* pre = nullptr, *cur = head;//初始化cur为尾节点,pre为尾节点的next
while (cur) {
//每一次循环,都将链表的一段反向
auto tmp = cur->next;//提前保存下一节点,防止链表断开
cur->next = pre;
pre = cur;
cur = tmp;
}
return pre;
}
};
2.递归:O(n),O(n):主要是n层递归栈耗费的空间
虽然不如方法1好,但思想很重要,先递归到最深处,再逐层返回的时候进行某些操作;
class Solution {
public:
ListNode* reverseList(ListNode* head) {
if (!head) return nullptr;//由于本题包含输入空头节点的测试例,才需要这一句,其实真正作为递归出口的只有下一句
if (!head->next) return head;//出口:递归到尾节点后,逐层把尾节点(即新的头节点)传递回去
//前面两句可以简化为:
//if (!head || !head->next) return head;//简洁但模糊了细节
auto ret = reverseList(head->next);//保存一下尾节点(新的头节点),并逐层传递; ret并不会用于反转操作
head->next->next = head;
head->next = nullptr; //让1->2->3->4->nullptr变成nullptr<-1<-2<-3<-4而不是1<-2<-3<-4
return ret;//逐层返回新的头节点(即原本的尾节点)
}
};
3.递归的另一种写法:O(n),O(n):主要是n层递归栈耗费的空间
这种递归写法类似于方法1,不同于方法2的先递归到最深处才逆向进行反转操作,这种递归是随着递归的深入直接进行反转操作;
class Solution {
private:
//这种递归写法,是随着递归的深入,直接逐层完成反转操作;
//而方法2的递归,是先递归到最深层,才开始逆向作反转操作
ListNode* reverse(ListNode* pre, ListNode* cur) {
//每一层递归,直接将传入的cur->next = pre
if (!head) return pre; //出口,用于得到新的头节点(即原本的尾节点)
auto tmp = cur->next;
cur->next = pre;
return reverse(cur, tmp);//这句相当于方法1中pre = cur, cur = tmp;
}
public:
ListNode* reverseList(ListNode* head) {
if (!head) return nullptr;
return reverse(nullptr, head);
}
};