LeetCode每日一题之链表反转

前言

大家好,今天是LeetCode每日一题的第八天,,给大家分享的是链表反转,难度系数两颗星!废话不多说,咱先上题目!

1.1 题目要求

题目类型: 链表反转

题目内容: 将单链表的链接顺序反转过来

测试案例:

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

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

注意事项: 使用两种方式解题!

1.2 解题方法

上面提到了实现链表反转,使用两种方式进行解题,那这两种方式是什么呢?让我们接着往下看!

1.2.1 使用迭代法
1.解题思路

核心思想:重复某一过程, 每一次处理结果作为下一次处理的初始值,这些初始值类似于状态, 每次处理都会改变状态, 直至到达最终状态

大致思路

  • 从前往后的遍历链表, 将当前节点的next指向上一个节点, 因此需要一个变量prev存储上一个节点;
  • 当前节点处理完后需要寻找下一个节点, 因此需要一个变量curr保存当前节点;
  • 处理完后要将curr(当前节点)赋值给prev(上一节点), 并将next指针赋值给curr(当前节点),因此需要一个临时变量提前保存下一个节点的next指针

链表反转处理过程

  1. 将curr(当前节点)的next指针保存到next临时变量, 即 next = curr.next
  2. 将prev(上一节点)赋值给curr(当前节点)的next指针, 即 curr.next = prev
  3. 本轮处理的curr(当前节点)在下一轮节点处理中变成了prev(上一节点),即prev = curr;
  4. 本轮处理结束后, 将next(临时变量)赋值给下一轮节点处理的curr(当前节点),即 curr = next;
1.2.2 代码实现
1.初次编写
  • 测试代码
package com.kuang.leetcode1;

/**
 * @ClassName ReverseList1
 * @Description 链表反转-使用迭代方式
 * @Author 狂奔の蜗牛rz
 * @Date 2021/8/26
 */

public class ReverseList1 {

    public static void main(String[] args) {
        //初始化5个链表节点, 指定val当前值和next指向节点
        //节点5
        ListNode node5 = new ListNode(5, null);
        //节点4
        ListNode node4 = new ListNode(4, node5);
        //节点3
        ListNode node3 = new ListNode(3, node4);
        //节点2
        ListNode node2 = new ListNode(2, node3);
        //节点1
        ListNode node1 = new ListNode(1, node2);
        //使用迭代方式进行链表反转(头结点为node1)
        ListNode prev = iterate(node1);
        //打印prev节点值
        System.out.println(prev);
    }

    //静态内部类ListNode(链表节点)
    static class ListNode {
       int val; //当前节点的对应值
       ListNode next; //next指针, 指向下一个链表节点

        /**
         * ListNode有参构造函数
         * @param val 当前节点值
         * @param next 下一链表节点
         */
       public ListNode(int val, ListNode next) {
           //初始化当前值
           this.val = val;
           //初始化next节点
           this.next = next;
       }
    }

    /**
     * 迭代反转链表元素
     * @param head 链表头结点
     * @return ListNode 链表节点
     */
    public static ListNode iterate(ListNode head) {
        //prev为链表中上一个节点(1是头结点, 它的上一节点值为null), next为存储下一个节点的临时变量
        ListNode prev = null, next;
        //curr是指当前节点(头结点)
        ListNode curr = head;

        /**
         * 这里涉及到要进行迭代(或者循环遍历), 那么是使用for循环还是while循环呢?
         * 由于链表的长度未知, 因此无法使用for循环来进行有限次循环,
         * 当前迭代方法只有一个head参数, 只包含head头结点的值和next指针,
         * 并不包含迭代器, 而for循环底层是依赖于迭代器的, 因此采用while循环
         */

        /**
         * 那么while循环的执行条件是什么呢?
         * curr(当前节点)值为null时, 说明执行到了链表的最后一个元素,
         * 只要curr值不为null,证明还需要继续执行循环
         * 因此while循环的执行条件为当前节点值不为null(即curr != null)
         */
         while (curr != null) {
            //将curr(当前节点, 值为1)的next(下一节点, 值为2)赋值给next(临时变量)
            next = curr.next;
            //将prev(上一节点, 初值为null)赋值给curr(当前节点, 值为1)的next(下一节点)
            curr.next = prev;
            //将pre和curr重新赋值, 为处理下一个节点做准备
            //本轮处理的curr(当前节点, 值为1)在下一轮节点处理中变成了prev(上一节点)
            prev = curr;
            //本轮处理结束后, 将next(临时变量, 值为2)赋值给下一轮节点处理的curr(当前节点)
            curr = next;
         }
        //将prev变量(此时该变量的值为上一轮循环中的当前节点)返回
        return prev;
    }

}

注意:如果编写算法题时没有思路,可以先根据题干编写出一个大致的代码框架, 然后在仔细分析题干和进一步调试,将代码补充完成

  • Debug测试

    将主方法中的最后两行代码打上断点,然后依次进行Debug测试

在这里插入图片描述

  • 测试结果

链表反转前

在这里插入图片描述

链表反转后

在这里插入图片描述

结果链表成功实现反转!

1.2.2 使用递归法
1.解题思路

核心思想

  • 以相似的方法重复, 类似于树结构, 先从根节点找到叶子节点, 从叶子节点开始遍历;

  • 把大的问题(整个链表)拆成性质相同的小问题(两个元素反转), 即curr.next.next = curr,
    将所有的小问题解决后, 大的问题也就解决了

问题思考

假设1为head头结点, 1的next值是2, 2的next值为3; 如果要实现链表反转, 需要把指向3的next指针断掉, 让2的next指针指向1,怎样实现呢?

解决方案:

因此需要设置一个next临时变量来存储head.next(值为2), 即head.next.next = head = 1;

但2的next指针指向1后, 1的next指针也指向了2, 这样会形成一个环形链表,怎样解决?

解决方案

让头结点1的next指针指向null, 即head.next = null

同理, 如果从链表尾部开始, 当前节点5的next指针指向4, 4的next指针指向null, 但是3的next指针并没有断开, 仍然指向4, 这样就可以确定4的位置

注意要点

  • 只需每个元素都执行curr.next.next = curr, curr.next = null 两个步骤即可
  • 为了保证链表不断开, 必须从最后一个元素开始
2.代码实现
  • 测试代码
package com.kuang.leetcode1;

/**
 * @ClassName ReverseList1
 * @Description 链表反转-使用递归方式
 * @Author 狂奔の蜗牛rz
 * @Date 2021/8/26
 */

public class ReverseList2 {

    public static void main(String[] args) {
        //初始化5个链表节点, 指定val当前值和next指向节点
        //节点5
        ListNode node5 = new ListNode(5, null);
        //节点4
        ListNode node4 = new ListNode(4, node5);
        //节点3
        ListNode node3 = new ListNode(3, node4);
        //节点2
        ListNode node2 = new ListNode(2, node3);
        //节点1
        ListNode node1 = new ListNode(1, node2);
        //使用递归方式进行链表反转(头结点为node1)
        ListNode prev = recursion(node1);
        //打印prev节点值
        System.out.println(prev);
    }

    //内部类ListNode
    static class ListNode {
        int val; //当前节点值
        ListNode next; //next指针, 指向下一节点

        /**
         * ListNode有参构造函数
         * @param val 当前节点值
         * @param next 下一链表节点
         */
        public ListNode(int val, ListNode next) {
            //初始化当前值和next节点
            this.val = val;
            this.next = next;
        }
    }

    /**
     * 递归反转链表元素
     * @param head 链表头结点
     * @return ListNode 链表节点
     */
    public static ListNode recursion(ListNode head) {
        /**
         * 问题1: 递归停止的条件是什么?
         * 如果head(头结点)值为null(说明链表为空, 因此无需反转),
         * 或者head(头结点)的next(下一节点)值为null(说明递归到了最后一个元素)
         */
        if(head == null || head.next == null) {
            //最后将head(头结点)进行返回
            return head;
            /**
             * 问题2:为什么要将head头结点返回呢?
             * 如果head头结点为null, 说明当前链表为空, 无需进行反转;
             * 如果head(头结点)的next(下一节点)值为null, 说明已经递归到了最后一个元素,所以将当前head结点返回
             */
        }
        //递归就是自己调用自己
        //假设head节点值为4, 那么head.next节点值便是5, 进行递归后, 返回的new_head节点值也为5
        ListNode new_head = recursion(head.next);
        //head(头结点)的next指针指向的next(下一节点)为头结点
        head.next.next = head;
        //head(头结点)的next(下一节点)值为null
        head.next = null;
        //将新建头结点进行返回
        return new_head;
        /**
         * 问题3:最后的返回值是什么?
         * 当第五个节点(值为5)进行递归时, 它的next(下一节点)值为null,
         * 因此就满足了递归的停止条件(head.next=null), 不再继续执行;
         * 链表反转结束后, 还需要将head头结点(此时值为5)进行返回
         */
    }
}
  • Debug测试

    将主方法中的最后两行代码打上断点,然后依次进行Debug测试

在这里插入图片描述

  • 测试结果

链表反转前

在这里插入图片描述

链表反转后

在这里插入图片描述

结果链表成功实现反转!

好了,今天LeetCode每日一题—链表反转到这里就结束了,欢迎大家学习和讨论,点赞和收藏!


参考视频链接:https://www.bilibili.com/video/BV1Ey4y1x7J3 (国内算法宝典-LeetCode算法50讲)

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

狂奔の蜗牛rz

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值