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
**实现代码**: