【每日力扣23】反转链表

一、题目[LeetCode-206]

给你单链表的头节点 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

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

二、思路

方法:迭代

使用一个栈按链表的遍历顺序存入链表指针ListNode*,栈可以维持逆序的访问顺序。然后再按逆序出栈,将指针的next进行反向更改即可。

/**
 * 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) {
        if(head == nullptr)
            return head;//处理空链表的退化情况
        stack<ListNode*> s;//使用栈按遍历顺序存储链表指针,维护逆序的访问顺序
        for(ListNode* curr = head; curr != nullptr; curr = curr->next)//入栈
        {
            s.push(curr);
        }
        ListNode* newHead = s.top();//反转之后的首元素节点
        ListNode* curr = newHead;//用来遍历的值针
        s.pop();
        if(!s.empty())
            curr->next = s.top();//初始化反转后的首元素节点
        while(!s.empty())
        {
            curr = s.top();
            s.pop();
            if(!s.empty())
                curr->next = s.top();
            else curr->next = nullptr;
        }
        return newHead;
    }
};

三、官方解法(来源:力扣(LeetCode))

方法一:迭代

假设链表为1→2→3→∅,我们想要把它改成 ∅←1←2←3。

在遍历链表时,将当前节点的 next 指针改为指向前一个节点。由于节点没有引用其前一个节点,因此必须事先存储其前一个节点。在更改引用之前,还需要存储后一个节点。最后返回新的头引用。

class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        ListNode* prev = nullptr;
        ListNode* curr = head;
        while (curr) {
            ListNode* next = curr->next;
            curr->next = prev;
            prev = curr;
            curr = next;
        }
        return prev;
    }
};

作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/reverse-linked-list/solution/fan-zhuan-lian-biao-by-leetcode-solution-d1k2/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

复杂度分析

  • 时间复杂度:O(n),其中 n 是链表的长度。需要遍历链表一次。

  • 空间复杂度:O(1)。

方法二:递归

递归版本稍微复杂一些,其关键在于反向工作。假设链表的其余部分已经被反转,现在应该如何反转它前面的部分?

假设链表为:

n_1\rightarrow n_2\rightarrow ... \rightarrow n_{k-1} \rightarrow n_k \rightarrow n_{k+1} \rightarrow ... \rightarrow n_m

若从节点n_{k+1}n_m已经被反转,而我们正处于n_k​。

n_1\rightarrow n_2\rightarrow...\rightarrow n_{k-1}\rightarrow n_k\rightarrow n_{k+1}\leftarrow...\leftarrow n_{m}

我们希望n_{k+1}的下一个节点指向n_k​。

所以,n_k.next.next=n_k

需要注意的是n_1的下一个节点必须指向∅。如果忽略了这一点,链表中可能会产生环。

class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        if (!head || !head->next) {
            return head;
        }
        ListNode* newHead = reverseList(head->next);
        head->next->next = head;
        head->next = nullptr;
        return newHead;
    }
};

作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/reverse-linked-list/solution/fan-zhuan-lian-biao-by-leetcode-solution-d1k2/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

复杂度分析

  • 时间复杂度:O(n),其中 n 是链表的长度。需要对链表的每个节点进行反转操作。
  • 空间复杂度:O(n),其中 n 是链表的长度。空间复杂度主要取决于递归调用的栈空间,最多为 n 层。

四、学习心得

对于链表递归的理解

类似于树的先序中序后序遍历,链表也有类似概念

  • 对链表的每个节点先操作再遍历,则相当于树的先序遍历。在链表中是从前往后操作
  • 对链表的每个节点先遍历再操作,则相当于树的后序遍历。在链表中时从后往前操作(如本题),先探测到底,再回溯
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值