反转链表II
题目描述
给定单链表的头指针 head 和两个整数 left 和 right ,其中 left <= right 。请你反转从位置 left 到位置 right 的链表节点,返回 反转后的链表 。
测试用例
输入:head = [1,2,3,4,5], left = 2, right = 4
输出:[1,4,3,2,5]
思路
- 1.找到待反转的区间
- 2.截取该区间进行反转
- 3.转完再拼接回去(假装无事发生[手动狗头])
区间反转不好处理,那咱至少会链表的整体反转啊,那就把它当作一个链表的整体反转来考虑,怎么考虑把要反转的区间截下,当成一条新链表处理。
如果你对链表的整体反转还不太熟悉,可以先看看这道题[反转链表]。
思路解析
有思路了,代码写起来也就容易些了。
1.定位left、right结点位置,根据给定的left、right确定需要反转的区间,记录下left的前驱结点、和right的后继结点。
记录前驱、后继结点是为了在后期拼接回来时,还能找到原来的“家”。
ListNode pre=dummy;
//找left位置的节点
for(int i=0;i<left-1;i++){
pre=pre.next;
}
//定位right位置的节点
ListNode post=pre;
for(int j=0;j<right-left+1;j++){
post=post.next;
}
//记录left、right原连接关系
ListNode leftNode=pre.next;
ListNode curr=post.next;
2.对截取的区间进行反转。
public static ListNode reverse(ListNode head){
if(head==null){
return null;
}
//因为头节点会发生变化,设置哨兵可以避免复杂情况的讨论
ListNode dummy=new ListNode(-1);
dummy.next=head;
ListNode curr=head;
ListNode pre=null;
while(curr!=null){
ListNode curNext=curr.next;
//尾节点作为新的头节点
if(curr.next==null){
dummy.next=curr;
}
//通过改变指针指向,从而实现链表反转
curr.next=pre;
pre=curr;
curr=curNext;
}
return dummy.next;
}
3.反转完成后再重新拼接回来
//将反转后的部分重新拼接回去
pre.next=post;
leftNode.next=curr;
return dummy.next;
整体代码
public class Main {
public static ListNode reverseBetween(ListNode head, int left, int right) {
//设置哑结点,避免复杂分类讨论
ListNode dummy=new ListNode(-1);
dummy.next=head;
ListNode pre=dummy;
//分别找到left、right指向的位置。
for(int i=0;i<left-1;i++){
pre=pre.next;
}
ListNode post=pre;
for(int j=0;j<right-left+1;j++){
post=post.next;
}
//记录left、right原连接关系
ListNode leftNode=pre.next;
ListNode curr=post.next;
//断开原链表连接
pre.next=null;
post.next=null;
//反转
reverse(leftNode);
//将反转后的部分重新拼接回去
pre.next=post;
leftNode.next=curr;
return dummy.next;
}
public static ListNode reverse(ListNode head){
if(head==null){
return null;
}
ListNode dummy=new ListNode(-1);
dummy.next=head;
ListNode curr=head;
ListNode pre=null;
while(curr!=null){
ListNode curNext=curr.next;
if(curr.next==null){
dummy.next=curr;
}
curr.next=pre;
pre=curr;
curr=curNext;
}
return dummy.next;
}
总结
链表题一定要画图!!!新手更要画图,不能偷懒!