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

 

进阶:链表可以选用迭代或递归方式完成反转。你能否用两种方法解决这道题?

#2.题解 ##2.1 迭代 假设链表为 1→2→3→∅,我们想要把它改成 ∅←1←2←3。 在遍历链表时,将当前节点的 next 指针改为指向前一个节点。由于节点没有引用其前一个节点,因此必须事先存储其前一个节点。在更改引用之前,还需要存储后一个节点。最后返回新的头引用。

作者:力扣官方题解
链接: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;
    }
};
posted @ 2023-10-28 16:21  DawnTraveler  阅读(25)  评论(0编辑  收藏  举报