• 翻转单链表细节讲解


    问题导入

    给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。

    在这里插入图片描述

    输入:head = [1,2,3,4,5]
    输出:[5,4,3,2,1]

    完整代码如下

    class Solution {
        // 迭代
        public ListNode reverseList1(ListNode head) {
            ListNode pre=null;
            ListNode cur=head;
            while(cur!=null){
                ListNode tmpNext = cur.next;
                cur.next=pre;
                pre=cur;
                cur = tmpNext;            
            }
            return pre;
        }
        // 递归
    
        /*
        看了半个小时可算是把这个递归看懂了!不妨假设链表为1,2,3,4,5。按照递归,当执行reverseList(5)的时候返回了5这个节点,reverseList(4)中的p就是5这个节点,我们看看reverseList(4)接下来执行完之后,5->next = 4, 4->next = null。这时候返回了p这个节点,也就是链表5->4->null,接下来执行reverseList(3),代码解析为4->next = 3,3->next = null,这个时候p就变成了,5->4->3->null, reverseList(2), reverseList(1)依次类推,p就是:5->4->3->2->1->null
        */
        public ListNode reverseList(ListNode head) {
            // 递归终止条件
            if(head==null||head.next==null){
                return head;
            }
        
            ListNode p = reverseList(head.next);
            // 它下一个的下一个改为本身
            // 也就是 原来是4->5  改为 5->4    head.next=5  5.next=4   再让4.next=null  p是5  再返回5  依次递归
            head.next.next = head;
            head.next = null;
            return p;
            
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    我们可以使用两个方法,下面我们对这两个方法进行讲解

    原理剖析

    迭代

    首先给你一个链表,我们给定一个pre

    pre=null;
    
    • 1

    当前的curr结点就是头结点

    curr = head;
    
    • 1

    在这里插入图片描述

    如果是翻转第一个,我们只需要

    curr.next = pre;
    
    • 1

    就可以了

    在这里插入图片描述
    然后让pre和curr都向后移动一位

    pre = 之前的curr;
    curr = 之前的curr的next;
    
    • 1
    • 2

    在这里插入图片描述
    之后再不断执行上面循环就行了

    curr.next = pre;
    
    • 1

    在这里插入图片描述

    pre = 之前的curr;
    curr = 之前的curr的next;
    
    • 1
    • 2

    直到最后一个翻转
    在这里插入图片描述
    最后的状态是这样的
    在这里插入图片描述

    有一个问题:怎么往后移动?

    这是理想的逻辑,但是我们在写代码的时候就会有一个问题:让pre与curr往后移动一位的时候,pre还好说,让pre = curr;就好了,但是已经执行了curr.next = pre;,curr怎么获取之前的curr的next呢?

    相信很多小伙伴们写代码的时候就困惑住了,就觉得这样行不通,然后就…放弃了

    别激动,既然没有,我们提前存一下就好了,设置一个临时结点去存储之前的curr的next

     ListNode tmpNext = cur.next;
    
    • 1

    然后之前的代码

    pre = 之前的curr;
    curr = 之前的curr的next;
    
    • 1
    • 2

    就可以替换为:

    // 存储之前的curr的next
    ListNode tmpNext = cur.next;
    // 翻转当前结点指向
    cur.next=pre;
    // 向下移动 pre与cur
    pre=cur;
    cur = next;      
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    然后我们需要不断执行这几个操作,所以他们要套在循环里:

    while(cur!=null){
        ListNode tmpNext = cur.next;
        cur.next=pre;
        pre=cur;
        cur = next;            
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    再开头我们需要定义pre与curr:

    ListNode pre=null;
    ListNode cur=head;
    
    • 1
    • 2

    最后我们只需要返回pre就可以了

    OK,完整代码就如下所示:

    // 迭代
    public ListNode reverseList1(ListNode head) {
        ListNode pre=null;
        ListNode cur=head;
        while(cur!=null){
            ListNode tmpNext = cur.next;
            cur.next=pre;
            pre=cur;
            cur = next;            
        }
        return pre;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    递归

    说完了上面的迭代法,下面我们说说递归,可能很多小伙伴会疑惑?这也能用递归???

    那是肯定的,我们先回顾一下递归的思想

    其实很简单,把递归两个字拆开就可以了:

    • 递:有相同的执行逻辑,能一层层的往下递推下去
    • 归:有终止逻辑,最终有相同的归操作,能一层层的向上返回来

    那么对于这一个链表的翻转,我们其实可以分解成两个子问题:

    • 翻转头结点
    • 翻转除了头结点之外的结点

    那么我们可不可以往下递呢?当然是可以的,我们可以把剩下的结点继续拆分

    • 翻转剩下链表的头结点
    • 翻转除了剩下链表的头结点之外的结点
      在这里插入图片描述
      那么他能不能归回来呢?当然是可以的,要归的话首先我们要有终止条件,终止条件我们往往看最后一个递归调用最为明显(也就是这个问题的最小拆分):

    最后是这样的:
    在这里插入图片描述
    我们可以发现当如下条件的时候就可以进行递归终止了

    head==null||head.next==null
    
    • 1

    有了终止条件,我们就需要完成递归中的归操作,我们进行翻转也是在归操作中进行的

    翻转操作如下,让当前头结点的next的next指向当前头结点,头结点的next指向null

    // 它下一个的下一个改为本身
    head.next.next = head;
    head.next = null;
    
    • 1
    • 2
    • 3

    最终代码如下:

     public ListNode reverseList(ListNode head) {
        // 递归终止条件
        if(head==null||head.next==null){
            return head;
        }
    
        ListNode p = reverseList(head.next);
        // 它下一个的下一个改为本身
        head.next.next = head;
        head.next = null;
        return p;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
  • 相关阅读:
    SLAM从入门到精通(lidar数据的采集)
    出入库效率提不上去?教你从0-1搭建WMS仓库管理系统
    javaScript操作数组的方法
    【c#】log4net用法
    (38)Verilog实现序列10111【状态机一段式】
    ISIS对IPv6的支持
    解密Prompt系列8. 无需训练让LLM支持超长输入:知识库 & unlimiformer & PCW & NBCE
    【驯服野生verilog-mode全记录】day2 —— 模块的例化
    SQLMaestro PHP Generator v2022 Crack
    猿创征文|Vue结合Vant-ui实现数据列表上拉加载更多,下拉刷新功能
  • 原文地址:https://blog.csdn.net/weixin_45525272/article/details/126417455