翻转单链表细节讲解

Posted 流楚丶格念

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了翻转单链表细节讲解相关的知识,希望对你有一定的参考价值。

文章目录

问题导入

给你单链表的头节点 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 = next;            
        
        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;
        
    

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

原理剖析

迭代

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

pre=null;

当前的curr结点就是头结点

curr = head;

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

curr.next = pre;

就可以了


然后让pre和curr都向后移动一位

pre = 之前的curr;
curr = 之前的curr的next;


之后再不断执行上面循环就行了

curr.next = pre;

pre = 之前的curr;
curr = 之前的curr的next;

直到最后一个翻转

最后的状态是这样的

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

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

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

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

 ListNode tmpNext = cur.next;

然后之前的代码

pre = 之前的curr;
curr = 之前的curr的next;

就可以替换为:

// 存储之前的curr的next
ListNode tmpNext = cur.next;
// 翻转当前结点指向
cur.next=pre;
// 向下移动 pre与cur
pre=cur;
cur = next;      

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

while(cur!=null)
    ListNode tmpNext = cur.next;
    cur.next=pre;
    pre=cur;
    cur = next;            

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

ListNode pre=null;
ListNode cur=head;

最后我们只需要返回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;

递归

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

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

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

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

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

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

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

  • 翻转剩下链表的头结点
  • 翻转除了剩下链表的头结点之外的结点

    那么他能不能归回来呢?当然是可以的,要归的话首先我们要有终止条件,终止条件我们往往看最后一个递归调用最为明显(也就是这个问题的最小拆分):

最后是这样的:

我们可以发现当如下条件的时候就可以进行递归终止了

head==null||head.next==null

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

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

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

最终代码如下:

 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;

以上是关于翻转单链表细节讲解的主要内容,如果未能解决你的问题,请参考以下文章

链表翻转的图文讲解(递归与迭代两种实现)

Java面试题目-单链表翻转(递归+非递归实现)

玩转单链表

Java算法 -- 单链表的反转单链表实现栈和队列以及双端队列K 个一组翻转链表

Java算法 -- 单链表的反转单链表实现栈和队列以及双端队列K 个一组翻转链表

Java算法 -- 单链表的反转单链表实现栈和队列以及双端队列K 个一组翻转链表