LeetCode - 206.反转链表

题目描述

反转一个单链表。

示例:

输入: 1->2->3->4->5->NULL
输出: 5->4->3->2->1->NULL

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

完整代码参考:GitHub 代码

题解

此题难度定级为简单,主要考察链表的基本操作。

解法1:递归

递归的方式代码实现较为简洁,但是过程稍微绕脑,分析如下:

0. 初始化 head => 1 => 2 => 3  => 4  => 5 => null,递归找出当前为 null 或 其下一个节点为 null 的节点,此时返回的节点是 5=> null;

1. 在递归条件中,需要判断 head 的下一个节点是否为 null,故 reverseList(head.next) 递归的入参为 head.next,否则将造成死循环(因为当前节点 head 及其下一个节点不为 null);

2. 定义新节点 newHead 指向递归的结果节点,此时 newHead =>5 => null,此时的 head => 4  => 5 => null;

3. 设置新节点(newHead )的下一个节点为新节点的上一个节点。什么意思呢?就是将 head 的尾节点再次指向 head,即构成一个无限的闭环,调试代码如下:

此时 head 与 newHead 指向的链表均构成环形链表。(为什么 newHead 指向的链表也是环形链表呢?因为 head 与 newHead 在递归的过程中自始至终操作的均是同一个链表。)

此时 head => 4 => 5 => 4 => 5 => 4 => .....

newHead  => 5 => 4 => 5 => 4 => .....

4. 设置 head.next = null,即此时 head =>4 => null,而 newHead => 5 => 4 => null;如下图:

5. 递归过程依次向前推进,head 变化过程如下:

head => 3 => 4 => 3 => 4 => .....

head => 2 => 3 => 2 => 3 => .....

head => 1 => 2 => 1 => 2 => .....

head => null

而 newHead 变化过程如下:

newHead => 5 => 4 => null

newHead => 5 => 4 => 3 => null

newHead => 5 => 4 => 3=> 2 => null

newHead => 5 => 4 => 3=> 2 => 1 => null

结束递归,返回 newHead,反转链表完成。

递归方式题解完整代码如下:

    /**
     * @Description: 反转链表 递归方式
     * 输入: 1->2->3->4->5->NULL
     * 输出: 5->4->3->2->1->NULL
     * @Date: 2019/12/4 22:33
     * @param: head
     * @ReturnType: linkedList.ListNode
     **/
    public static ListNode reverseList(ListNode head) {
        // 当节点为空,或节点是尾节点时,返回当前节点
        if (head == null || head.next == null) {
            return head;
        }

        // 递归寻找尾节点
        ListNode newHead = reverseList(head.next);
        // 设置尾节点(新节点)的下一个节点为尾节点的上一个节点
        head.next.next = head;
        // 经过上一步,在尾节点与尾节点的上一个节点间形成死循环,都是对方的下一个节点;
        // head.next = null 设置尾节点的上一个节点指向 null,打破死循环,此时尾节点指向尾节点的上一个节点,完成第一步反转;
        head.next = null;
        // 递归后新节点为之前的尾节点,从尾到头依次反转完成
        return newHead;
    }

解法2:非递归

非递归方式相较于递归方式略显复杂,用到了三个指针,不断改变指针的指向,一步步推出结果。

推导过程

0. 首先判断头指针 head 是否为 null,或者 head 的下一个节点是否为空;

1. 定义空节点 prev;当前节点 cur 为 head 指向的节点;

  此时 prev = null, cur => 1 => 2 => 3  => 4  => 5 => null

2. 此时 cur 为新的链表,也是主要的操作对象,当 cur 不为 null 时进入循环;

3. 截取 cur 第一个节点之后的链表赋予 next,此时 next => 2 => 3  => 4  => 5 => null;

4. 将 prev(null)赋予 cur.next,此时 cur => 1 => null;

5. 将 cur 赋予 pev,此时 pev => 1 => null;

6. 最后将 next 赋予 cur,此时 cur => 2 => 3  => 4  => 5 => null;结束第一次循环;

当进入下一次循环时,next 指向的链表长度不断缩减,cur 指向的链表在循环中反转后赋予 prev,使得 prev 指向的链表作为反转结果。

各个节点指向的链表变化单步调试如下:


初始:
perv = null
cur => 1 => 2 => 3 => 4 => 5 => null

第一次循环:
next => 2 => 3 => 4 => 5 => null
cur => 1 => null
pev => 1 => null
cur => 2 => 3 => 4 => 5 => null

第二次循环:
next => 3 => 4 => 5 => null
cur => 2 => 1 => null
pev => 2 => 1 => null
cur => 3 => 4 => 5 => null

第三次循环:
next => 4 => 5 => null
cur => 3 => 2 => 1 => null
pev => 3 => 2 => 1 => null
cur => 4 => 5 => null

第四次循环:
next => 5 => null
cur => 4 => 3 => 2 => 1 => null
pev => 4 => 3 => 2 => 1 => null
cur => 5 => null

第五次循环:
next => null
cur => 5 => 4 => 3 => 2 => 1 => null
pev => 5 => 4 => 3 => 2 => 1 => null
cur => null

结束循环。输出:pev => 5 => 4 => 3 => 2 => 1 => null

非递归方式题解完整代码如下:

    /**
     * @Description: 反转链表 非递归方式
     * 用三个指针 prev,cur,next ,紧紧相邻,不断前进;
     * 每次将 cur.next 指向 prev ,将 prev 指向 cur, cur 指向 next
     * @Date: 2019/12/4 22:00
     * @param: head
     * @ReturnType: linkedList.ListNode
     **/
    public static ListNode reverseListTwo(ListNode head) {
        if (head == null || head.next == null) {
            return head;
        }

        ListNode prev = null;
        ListNode cur = head;
        while (cur != null) {
            // 截取第一个节点之后的链表串赋予 next
            ListNode next = cur.next;
            // 取 cur 的第一个节点指向 prev,用来拼接反向链表
            cur.next = prev;
            // 拼接后的结果赋予 prev
            prev = cur;
            // 将 next 赋予 cur,下次循环截取第一个节点
            cur = next;
        }
        return prev;
    }

总结

1. 算法题必须动手实践,不能止步于看懂、听懂;

2. 单步调试非常必要,研究参数值的变化非常必要。一定要调试,一定要调试,一定要调试!!!

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值