【LeetCode】链表反转

不管现实多么惨不忍睹,都要持之以恒地相信,这只是黎明前短暂的黑暗而已。不要惶恐眼前的难关迈不过去,不要担心此刻的付出没有回报,别再花时间等待天降好运。真诚做人,努力做事!你想要的,岁月都会给你。【LeetCode】链表反转,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com,来源:原文

题目

题目:给定单链表头节点,将单链表的链接顺序反转过来
例:
输入:1->2->3->4->5
输出:5->4->3->2->1
要求:按照两种方式实现

解决办法

方式一:(直接迭代法)

思路

单链表的结构如下所示:
在这里插入图片描述
如图所示。我们分析一下,如果要完成翻转需要思考什么:

  1. 循环遍历所有节点,直到结束。所以需要确认结束条件
  2. 安全地将当前节点指向它的前驱节点(直接指向的话会导致链表遍历失败,如下所示,找不到下一个操作节点了)
    在这里插入图片描述

对于第1点,既然要循环遍历,那无非就是迭代器或者循环了,显然这里没办法使用迭代器,所以考虑使用while循环。何时结束呢?不难发现,就是【当前节点的next节点为null的时候】,或者我们新增一个节点currNode,表示当前操作节点,它随着遍历深度,不断往后移。显然它初始值是head头节点,所以可以使用currNode == null表示遍历结束了。
对于第2点,如何才能安全地将当前节点指向它的前驱节点呢?正常来说需要以下2步才能保证:

  1. 记录当前节点原有的next指向节点,这里记录为oldNextNode (原因见上图)
  2. 还需要一个前驱节点preNode记录下一个节点需要设置的前驱节点(也可以看上图,除了找不到next节点以外,其实next节点也找不到前面的节点了,所以也需要记录)

准备好了我们就可以尝试模拟了。
在这里插入图片描述
操作步骤:

  1. 设置oldNextNodecurrNode的next节点
  2. currNode的next节点指向preNode
  3. preNode设置为currNode
  4. currNode设置为oldNextNode
    在这里插入图片描述
    似乎没问题,那我们开始写代码吧

代码示例

代码如下:

public class ReverseLinkedList {
    public static void main(String[] args) {
        ListNode node5 = new ListNode(5, null);
        ListNode node4 = new ListNode(4, node5);
        ListNode node3 = new ListNode(3, node4);
        ListNode node2 = new ListNode(2, node3);
        ListNode node1 = new ListNode(1, node2);

        reverseLinkedList(node1);
    }

    private static void reverseLinkedList(ListNode head) {

        // 初始化我们的中间节点
        ListNode currNode = head;
        ListNode oldNextNode = null;
        ListNode preNode = null;

        // 循环遍历,currNode != null 则表示还没遍历到结尾
        while (currNode != null) {
            oldNextNode = currNode.next;
            currNode.next = preNode;
            preNode = currNode;
            currNode = oldNextNode;
        }

        System.out.println("试试看");
        System.out.println(preNode);
    }
}

最后我们看看结果吧:
在这里插入图片描述
倒过来了!

方式二:(递归法)

思路

(PS:既然说是递归解决了,那我们得先知道,递归能做什么。可以这么说,凡是能用递归解决的问题,一定是能将某个大问题,划分成N个存在共性的小问题。所以,我们可以朝这个方向看看。)

还是看着单链表的结构图来思考:
在这里插入图片描述
我们发现,如果从最后一个节点开始出发的话,这个题目的问题就会变成了另一个问题:我的前驱节点是谁?
在正常的遍历中,我们使用preNode状态节点来记录前驱节点,但在递归里面就没那么麻烦了!因为在正向递归遍历节点的时候,当前节点的结束,肯定是在前驱节点的调用处。效果图如下:
在这里插入图片描述
递归代码如下所示:

    public static void main(String[] args) {
        ListNode node5 = new ListNode(5, null);
        ListNode node4 = new ListNode(4, node5);
        ListNode node3 = new ListNode(3, node4);
        ListNode node2 = new ListNode(2, node3);
        ListNode node1 = new ListNode(1, node2);

//        reverseLinkedList(node1);
        recursion(node1);
    }

    private static ListNode recursion(ListNode curr) {

        // 递归出口(遍历到最后了)
        if (curr == null || curr.next == null) {
            return curr;
        }

        ListNode recursion = recursion(curr.next);
        System.out.println(recursion.value);
        return curr;
    }
//    系统输出:
//            5
//            4
//            3
//            2

既然知道了这个特点,我们只需要做如下步骤就好了:

  1. 每次递归调用,返回当前节点(这是为了创造当前节点能跟前驱节点在同一个代码块里的环境)
  2. 获得递归调用返回的节点recursion 之后,使用recursion .next = curr; curr.next = null;即可
    在这里插入图片描述

代码示例

   public static void main(String[] args) {
        ListNode node5 = new ListNode(5, null);
        ListNode node4 = new ListNode(4, node5);
        ListNode node3 = new ListNode(3, node4);
        ListNode node2 = new ListNode(2, node3);
        ListNode node1 = new ListNode(1, node2);

//        reverseLinkedList(node1);
        recursion(node1);
        System.out.println(node5);
    }

    private static ListNode recursion(ListNode curr) {

        // 递归出口(遍历到最后了)
        if (curr == null || curr.next == null) {
            return curr;
        }

        ListNode recursion = recursion(curr.next);
        recursion.next = curr;
        curr.next = null;
        return curr;
    }

最后我们看看结果吧:
在这里插入图片描述

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/180526.html

(0)
飞熊的头像飞熊bm

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!