题目描述
完成指定区间内的链表反转
比如原链表为:1–>2–>3–>4–>5–>6–>null
要求把位置从2到4的链表反转。
所以反转结果为:1–>4–>3–>2–>5–>6–>null
前置知识
本题是反转整个链表的升级版,建议先了解下简单的反转整个链表的方式。
基本思路
在有了前面反转整个链表的基础上,只需稍作修改,我们的想法是如果能把待反转的链表的截取出来,完成反转后,再拼回去,不就可以了嘛
也就是把2–>3–>4截取出来,变为如下样子:
1–>2–> 头尾断开 -->6–>null
然后反转2–>3–>4,得到:4–>3–>2,再拼接回去得到:1–>4–>3–>2–>5–>6–>null
示例代码
public class Code_Reverse_2 {
public static void main(String[] args) {
ListNode n = new ListNode(1);
ListNode head = n;
for (int i = 2; i < 6; i++) {
ListNode c = new ListNode(i);
n.next = c;
n = n.next;
}
System.out.println("反转之前:" + head);
ListNode r = reverseBetween(head, 2, 4);
System.out.println("反转滞后:" + r);
}
public static ListNode reverseBetween(ListNode head, int left, int right) {
//设置一个哑节点,以免头节点发生变化,本例中,头节点未发生变化,所以实际上也可以不用哑节点
ListNode dummyNode = new ListNode(-1);
dummyNode.next = head;
ListNode leftPre = dummyNode;
//leftPre来到left的前一个位置
for (int i = 0; i < left - 1; i++) {
leftPre = leftPre.next;
}
ListNode rightNode = leftPre;
//rightNode来到right当前位置
for (int i = 0; i < right - left + 1; i++) {
rightNode = rightNode.next;
}
//left位置当前的节点
ListNode leftNode = leftPre.next;
//right位置的下一个节点
ListNode rightNext = rightNode.next;
//头断开链表
leftPre.next = null;
//尾断开链表
rightNode.next = null;
//反转链表
reverseList(leftNode);
//头连上链表
leftPre.next = rightNode;
//尾连上链表
leftNode.next = rightNext;
return dummyNode.next;
}
/**
* 反转链表
* @param head
* @return
*/
private static ListNode reverseList(ListNode head) {
ListNode pre = null;
ListNode next;
while (head != null) {
next = head.next;
head.next = pre;
pre = head;
head = next;
}
return pre;
}
}
代码图解
扫描二维码关注公众号,回复:
12894425 查看本文章
这种方式如果反转链表的区间的范围很大,比如就是一头一尾,那么就意味着需要完整遍历一次链表,接着在反转时又需要遍历一次,那么有没有只遍历一次就完成反转的方式呢?答案是有的。
只一次遍历的方式
示例代码
public static ListNode reverseBetween(ListNode head, int left, int right) {
ListNode dummyNode = new ListNode(-1);
dummyNode.next = head;
ListNode leftPre = dummyNode;
for (int i = 0; i < left - 1; i++) {
leftPre = leftPre.next;
}
//cur节点初始化指向leftPre的下一个节点(在之后的反转过程中不会变)
ListNode cur = leftPre.next;
//始终指向cur的下一节点
ListNode next;
for (int i = 0; i < right - left; i++) {
next = cur.next;
cur.next = next.next;
next.next = leftPre.next;
leftPre.next = next;
}
return dummyNode.next;
}
代码图解:
用红线标记当前链表节点之间的关系,可以看出节点3已经来到了节点2的前面(头插法)
“拉直” 了看
第二次循环开始,next移动一位,cur不变
第二次循环完成后,当前链表节点之间的关系,节点4也来到了节点3的前面(头插法)
最终,我们通过一次遍历完成了链表的反转。