[链表] 206

post = head;


最后在进入下一轮反转之前,取出之前备份的 temp 覆盖 head,相当于取出原链表的下一个节点作为工作节点,准备做下一轮反转,直到head == null :



head = temp;


(算法流程讲起来很乱,看代码自己模拟一遍过程就懂了)


**实现代码**:



class Solution {
public ListNode reverseList(ListNode head) {
ListNode post = null;
while(head != null){
ListNode temp = head.next;//备份当前节点的原next域
head.next = post;//更新当前节点反转后的next域
post = head;//将当前节点作为post供下一个节点的next域使用
head = temp;//更新head
}
return post;
}
}


#### 思路2:递归实现(更难理解,更巧妙)


[参考链接:https://leetcode-cn.com/problems/reverse-linked-list-ii/solution/bu-bu-chai-jie-ru-he-di-gui-di-fan-zhuan-lian-biao/]( )


反转递归实现的要点:用内部栈来保存head。



例如:1->2->3->4->5->NULL


每进入一层递归就是取链表的下一个节点,深入到最深层时,head=5,head.next=null,记录链表的尾结点post=head=5,然后把该尾结点post返回给上一层。


上一层对应的 head=4,head.next=5,则head.next.next=head表示5->4,相当于把4->5反转为5->4。然后仍然返回post=5,相当于把5->4视为反转后的新链表,把反转后的头结点返回给上一层了。


其他节点以此类推,反转链表在递归一层层向上返回的过程中不断扩展,最终得到反转后的完整链表。


* **注意**:每一层得到的反转链表都要确保是有头有尾的链表,避免节点和旧链表有联系造成未知环路,所以每有一个新节点加入到反转链表的末尾,就将它的next置为null,避免出现环路:  
 例如:上例中得到5->4后,需要再令4.next=null,得到当前构造中的反转链表:5->4->null;  
 继续返回上一层,此时的head=3,head.next=4,head.next.next=head,相当于5->4->3,再置3->null,得到5->4->3->null。  
 以此类推。
* 增加了这一步也解决了最终反转链表的尾结点的next域置null的处理。


**递归返回值**:  
 这里递归返回值的作用比较独特。


每一层递归都能从内部栈里得到当前层对应的head,因为最终要返回的是反转后链表的头结点,也就是原链表的尾结点,所以每一层递归返回给上一层的都是最深层递归得到的尾结点post。


递归的返回值并没有参与反转,而是始终指向原链表的尾结点,作为反转后链表的头结点,以便最后的返回。


**实现代码**:



class Solution {
public ListNode reverseList(ListNode head) {
if(head.next == null) return head;
ListNode post = reverseList(head.next);//递归最深层的head=4,post=5
head.next.next = head;
head.next = null;//不影响中间节点的反转,但可以让头结点反转变为尾结点后置next域为null,让反转链表变得完整
return post;
}
}


### 92. 反转链表 II(反转部分链表)


[题目链接:https://leetcode-cn.com/problems/reverse-linked-list-ii/]( )


**分类:链表(反转部分链表:迭代实现、递归实现)**


![在这里插入图片描述](https://img-blog.csdnimg.cn/20200921151004949.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L20wXzM4MTQyMDI5,size_16,color_FFFFFF,t_70#pic_center)


#### 题目分析:(很好的递归练习题)


本题的递归实现更难,也更巧妙,可以作为对递归很好的训练题目。


相似题目:206. 反转链表,206反转的是整个链表,当本题的m=1,n=链表长度,问题就退化为206题。


反转部分链表和反转整个链表的区别在于:反转完部分链表后,还需要将反转后这部分插回原链表中,所以需要记录反转部分的前一个节点和后一个节点,以便回插。


#### 思路1:一趟扫描 + 迭代实现


特殊用例:m=n表示待反转链表只有一个点,返回原链表即可。


设置虚拟头结点dummy,设置一个指针start,用于指向第m个节点的前一个节点,目的在于当m~n的节点反转结束后,要将反转后的链表插入回原链表,需要这个指针和工作指针head来标记反转链表的边界。


同时设置一个指针post, 表示反转后节点.next指向的对象,具体作用和206题的post相同。


##### 算法流程


设置一个计数器count,初始时count=1,start指向dummy,post指向head:



//三个指针的初始状态
dummy->1->2->3->4->5->6->NULL,m=2,n=5
↑ ↑
start post/head


当count < m时,start,post,head同步后移,每移动一位,count+1;


当count == m时,说明找到待反转部分的第一个节点,start不再移动,此时start指向的就是待反转链表的前一个节点。开始执行反转操作:此时head指向待反转部分的第一个节点,post = head。因为待反转链表的最后一个节点的next目前是未知的,不像206题已知为null,所以post初始化时随意赋一个值。


一个节点的反转操作:



备份节点的原next域:
ListNode temp = head.next;
head.next = post;
post = head;
head = temp;

至此一个节点反转完毕,count+1.


当count==n时,说明待反转链表内部已经反转完毕,此时的链表状态为:



//待反转部分反转完毕后是三个指针的状态
dummy->1 5->4->3->2 6->NULL,m=2,n=5
↑ ↑ ↑
start post head


剩下的就是将反转后的链表插入原链表中:


* 注意:此时start.next=2节点,即start.next指向的是反转后反转部分的最后一个节点。



ListNode tail = start.next;
start.next = post;
tail.next = head;

得到:



//将反转后部分插入原链表中:
dummy->1->5->4->3->2->6->NULL,m=2,n=5
↑ ↑ ↑ ↑
start post tail head


反转完毕。


**实现代码**:



class Solution {
public ListNode reverseBetween(ListNode head, int m, int n) {
//特殊用例
if(m == n) return head;

    //虚拟头结点
    ListNode dummy = new ListNode(0);
    dummy.next = head;
    ListNode start = dummy, post = head;
    int count = 1;//计数器
    //反转m~n的节点
    while(count <= n){
    	//寻找第m个节点
        if(count < m){
            start = start.next;
            post = post.next;
            head = head.next;
        }
        //count>=m,开始反转操作
        else{
            ListNode temp = head.next;
            head.next = post;
            post = head;
            head = temp;
        }
        count++;
    }
    //将反转后的链表插入原链表中
    ListNode tail = start.next;
    start.next = post;
    tail.next = head;
    return dummy.next;
}

}


#### 思路2:一次扫描 + 递归实现(基于 反转链表前n个节点)


参考:<https://leetcode-cn.com/problems/reverse-linked-list-ii/solution/bu-bu-chai-jie-ru-he-di-gui-di-fan-zhuan-lian-biao/>


首先,回顾206.**反转整个链表**的递归实现思路;


接着,先学习**反转前n个节点**的递归实现:


和反转整个链表相比,反转前n个节点,首部的处理不变,变的是反转部分尾结点的处理。在反转整个链表的题目中,原链表的头结点反转后变为最后一个节点,所以它的next要指向Null,但反转前n个节点的题目中,原链表的头节点反转后,它的next 要指向 第 n + 1 个节点。也就是说,第n个节点的反转需要特殊处理。


而且反转后链表的头结点也是 原链表的第 n 个节点,所以递归返回值仍然是反转后链表的头结点,也就是原链表的第 n 个节点。


**那么,如何寻找第 n 个节点?**


递归函数的参数给定了 题目要求的n,即反转前n个节点。我们可以利用这个参数,每进入一层递归 n - 1,当 n == 1时,当前层递归的head指向的就是原链表的第n个节点。



例如:
1->2->3->4->5->6->NULL , n=5

head ,n=5

每进入一层递归,head = head.next,n-- …

1->2->3->4->5->6->NULL , n=5

head ,n=1


**实现代码**:



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值