其他分享
首页 > 其他分享> > 链表题目:反转链表

链表题目:反转链表

作者:互联网

文章目录

题目

标题和出处

标题:反转链表

出处:206. 反转链表

难度

3 级

题目描述

要求

给你单链表的头结点 head \texttt{head} head,请你反转链表,并返回反转后的链表。

示例

示例 1:

示例 1

输入: head   =   [1,2,3,4,5] \texttt{head = [1,2,3,4,5]} head = [1,2,3,4,5]
输出: [5,4,3,2,1] \texttt{[5,4,3,2,1]} [5,4,3,2,1]

示例 2:

示例 2

输入: head   =   [1,2] \texttt{head = [1,2]} head = [1,2]
输出: [2,1] \texttt{[2,1]} [2,1]

示例 3:

输入: head   =   [] \texttt{head = []} head = []
输出: [] \texttt{[]} []

数据范围

进阶

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

解法一

思路和算法

原始链表中,每个结点的 next \textit{next} next 指针都指向后一个结点。反转链表之后,每个结点的 next \textit{next} next 指针都指向前一个结点。因此,反转链表需要改变每个结点的 next \textit{next} next 指针指向的结点。

由于单链表中的每个结点都只有指向后一个结点的 next \textit{next} next 指针,因此需要另外存储前一个结点,在遍历链表的过程中完成反转。

具体做法是,维护两个变量 prev \textit{prev} prev 和 curr \textit{curr} curr 分别表示前一个结点和当前结点,初始时 prev = null \textit{prev} = \text{null} prev=null, curr = head \textit{curr} = \textit{head} curr=head。改变 curr \textit{curr} curr 的 next \textit{next} next 指针指向时,需要先得到其后面一个结点 next \textit{next} next,然后令 curr . next \textit{curr}.\textit{next} curr.next 指向 prev \textit{prev} prev,再将 prev \textit{prev} prev 和 curr \textit{curr} curr 都向后移动一步。对于链表中的每个结点进行上述操作,当 curr \textit{curr} curr 变成 null \text{null} null 时,遍历结束,此时完成链表的反转, prev \textit{prev} prev 指向反转后的链表的头结点。

下图为反转链表的例子。对于每个结点,需要将指向后一个结点的 next \textit{next} next 指针变成指向前一个结点。虚线表示 next \textit{next} next 指针原来的指向。

图 1

代码

class Solution {
    public ListNode reverseList(ListNode head) {
        ListNode prev = null, curr = head;
        while (curr != null) {
            ListNode next = curr.next;
            curr.next = prev;
            prev = curr;
            curr = next;
        }
        return prev;
    }
}

复杂度分析

解法二

思路和算法

也可以使用递归的方法反转列表。迭代的方法较简单,递归的方法相对复杂。

对于递归问题,首先需要考虑终止条件。这道题的终止条件是链表为空或者只有 1 1 1 个结点,此时不需要反转,直接返回链表即可。

当链表至少有 2 2 2 个结点时,首先将除了头结点以外的部分反转,然后改变头结点和头结点后面一个结点的 next \textit{next} next 指针的指向。

例如,原始链表为:

1 → 2 → 3 1 \rightarrow 2 \rightarrow 3 1→2→3

到达结点 3 3 3 时为递归的终止条件,回到结点 2 2 2。

到达结点 2 2 2 时, 3 3 3 已经反转完毕,将 3 3 3 的 next \textit{next} next 指针变成指向 2 2 2,将 2 2 2 的 next \textit{next} next 指针变成指向 null \text{null} null。此时链表为:

1 → 2 ← 3 1 \rightarrow 2 \leftarrow 3 1→2←3

到达结点 1 1 1 时, 2 2 2 和 3 3 3 已经反转完毕,将 2 2 2 的 next \textit{next} next 指针变成指向 1 1 1,将 1 1 1 的 next \textit{next} next 指针变成指向 null \text{null} null。此时链表为:

1 ← 2 ← 3 1 \leftarrow 2 \leftarrow 3 1←2←3

反转链表结束。

需要注意的是,递归过程中将当前结点的 next \textit{next} next 指针变成指向 null \text{null} null 是不可缺少的操作,否则链表中会出现环。

代码

class Solution {
    public ListNode reverseList(ListNode head) {
        if (head == null || head.next == null) {
            return head;
        }
        ListNode second = head.next;
        ListNode newHead = reverseList(second);
        second.next = head;
        head.next = null;
        return newHead;
    }
}

复杂度分析

解法三

思路和算法

解法二使用递归,空间复杂度是 O ( n ) O(n) O(n)。如果使用尾递归,则可以将空间复杂度优化至 O ( 1 ) O(1) O(1)。

尾递归的含义是,一个方法或函数中所有递归形式的调用都出现在方法或函数的末尾,则这个递归方法或函数是尾递归的。当递归调用是整个方法或函数体中最后执行的语句,且返回值不属于表达式的一部分时,递归调用就是尾递归。尾递归的特点是在回归过程中不用做任何操作,当编译器检测到尾递归时,会覆盖当前的栈帧而不是新建一个栈帧,从而优化栈空间。

这道题可以使用尾递归实现。使用尾递归实现,需要创建一个辅助方法,该辅助方法包含两个参数,分别是前一个结点 prev \textit{prev} prev 和当前结点 curr \textit{curr} curr。尾递归的具体实现如下:

在主方法中调用尾递归方法时,参数为 prev = null \textit{prev} = \text{null} prev=null, curr = head \textit{curr} = \textit{head} curr=head,反转后的链表的头结点即为调用尾递归方法的返回值。

代码

class Solution {
    public ListNode reverseList(ListNode head) {
        return reverseListTail(null, head);
    }

    public ListNode reverseListTail(ListNode prev, ListNode curr) {
        if (curr == null) {
            return prev;
        }
        ListNode next = curr.next;
        curr.next = prev;
        return reverseListTail(curr, next);
    }
}

复杂度分析

标签:结点,curr,反转,next,链表,textit,题目,prev
来源: https://blog.csdn.net/stormsunshine/article/details/120591732