提示:人啊,果然跟花一样,开花前的等待无比漫长,绽放的魅力却转瞬即逝。
参考题目:92. 反转链表 II - 力扣(LeetCode)
给你单链表的头指针 head 和两个整数 left 和 right ,其中 left <= right 。请你反转从位置 left 到位置 right 的链表节点,返回 反转后的链表 。
当然我们上次已经教大家怎么将一个链表翻转过来了,今天我们就来看看他的变题(当然难度会更高
依旧还是两种解法:
我们姑且叫他头插法和穿针引线法(主要还是不会起名字:
如果left和right的范围很大的,正好是头节点和尾节点的话,找到left和right需要遍历一次,
反转他们的话还需要一次遍历,这样的话复杂度为O(N),但是需要遍历两次,想一下有没有遍历一次的方法有的,当然可以这样做;
整体步骤:
/**
* 方法1:头插法
*
* @param head
* @param left
* @param right
* @return
*/
public static ListNode reverseBetween2(ListNode head, int left, int right) {
// 设置虚拟头节点
ListNode dummyNode = new ListNode(-1);
dummyNode.next = head;// 用一在保留引用
ListNode pre = dummyNode;
for(int i = 0; i < left - 1; i++){
pre = pre.next;
} // 找到left的前一个节点
ListNode cur = pre.next;
ListNode next = null;
for(int i = 0; i < right - left; i++){
next = cur.next;
cur.next = next.next;
next.next = pre.next; // 这一点不太理解
pre.next = next;
}
return dummyNode.next;
}
这种方法思路很简单,但是书写起来很难,需要你对链表有一定的熟悉程度,话不多说,我们开始尝试一下吧。
首先看下图:
/**
* 基本的反转方法
*
* @param head
* @return
*/
public static ListNode reverseList(ListNode head) {
ListNode pre = null;
ListNode cur = head;
while(cur != null) {
ListNode next = cur.next;
cur.next = pre;
pre = cur;
cur = next;
}
return pre;
}
/**
* 方法2:穿针引线法
*
* @param head
* @param left
* @param right
* @return
*/
public static ListNode reverseBetween(ListNode head, int left, int right) {
// 因为头节点频繁变化,使用虚拟节点可以避免这样的问题
ListNode dummyNode = new ListNode(-1);
dummyNode.next = head;
ListNode pre = dummyNode;
// 第一步找到left 和 righe 节点
// 让per 做到left的前一个节点
for(int i = 0; i < left - 1; i++){
pre = pre.next;
}
ListNode rightNode = pre;
// 第二步 pre 继续前进 right - left + 1 来到right节点
for(int i = 0; i < right - left + 1; i++){
rightNode = rightNode.next;
}
// 第三步 切出子链表
ListNode leftNode = pre.next;
ListNode cucc = rightNode.next;
// 切断连接
pre.next = null;
rightNode.next = null;
// 第四步 反转链表
reverseList(leftNode);
// 第五步 更改指针域(接回原来的链表)
pre.next = rightNode;
leftNode.next = cucc;
return dummyNode.next;
}
这里需要重点理解一下反转链表,注意指针域的变化