题目描述
给你单链表的头指针 head 和两个整数 left 和 right ,其中 left <= right 。请你反转从位置 left 到位置 right 的链表节点,返回 反转后的链表 。
分析
输入:链表头节点 head
输出:返回翻转之后的链表头节点
解题思路
如果我们有一个链表从 1 到 5,left
在 2, right
在 4,我们需要翻转的部分在 2-4 之间这段
在这里我们也需要记录下这个区间的前驱节点 pre
, 后继节点 succ
。这是为了翻转之后,把前驱节点的 next
和翻转之后的这部分的最后一个节点的 next
指向正确的节点。
这里需要注意的一点是,left
, right
,只是一个 index,并没有直接给我们真实的节点啊。所以还需要我们自己去找这两个节点,我把他们命名为 leftNode
, rightNode
, 以及 pre
, succ
, 才能做对应的操作。
那实际上就是要找 pre
, rightNode
, leftNode
, succ
就是他们的 next 指向。
为了少做判断,我们引入一个 dummyHead
,将它的 next
指向 head
。我们的查找过程从 dummyHead
开始。
OK,那先找 pre
, 现在问题的关键就是,要从 dummyHead
移动几步,能够得到正确的节点?
这个过程我建议大家用 for 循环来实现,因为你要走的步数,完全可以作为 for 终止的条件,我们举个例子🌰:
比如我要走两步,
for (let i = 0; i < 2; i++) {
console.log(`I have moved '${i + 1}' steps`)
}
很明显上面 for 循环的语句执行了两次,那如果每次变量都在链表里走一步,就会移动移动两步。可以证明这个过程是正确的。
我们运用这个原理去观察 left
,pre
的对应关系。
pre
与 left
差 1,如果 left
在 2, 那么找到 pre
的过程是这样的:
dummyHead
➡️ 1
(head)
实际上就是走一步,left - 1
那么找到 pre
的 code 就是:
for (let i = 0; i < left - 1; i++) {
pre = pre.next
}
那对于 rightNode
,我们如法炮制,观察一下 right
与 pre
,
right
在 4, prev
在 1, right
与 left
差2,
那么对于已经找到的 pre
, 需要走 right - left + 1
。
对应的代码就是:
rightNode = pre
for (let i = 0; i < right -left + 1; i++) {
rightNode = rightNode.next
}
找到了他们,我们也就找到了 leftNode
, succ
对于翻转的过程,我们也有一个固定的模版去解决:
function reverseList(head) {
// 拿前一个节点和当前节点进行位置交换
let pre = null,
cur = head
// 只要还有节点要交换,就进行下去
while (cur) {
// 因为要把当前节点的 next 指向前一个节点,所以用一个变量先把当前的 next 存起来~
const tmp = cur.next
cur.next = pre
pre = tmp
cur = cur.next
}
}
最后再把这个反转后的链表和外边的剩余部分连接起来就行了,也就是和 pre
, succ
连接起来。
我们直接看整体的代码
代码
/**
* Definition for singly-linked list.
* function ListNode(val, next) {
* this.val = (val===undefined ? 0 : val)
* this.next = (next===undefined ? null : next)
* }
/**
* @param {ListNode} head
* @param {number} left
* @param {number} right
* @return {ListNode}
*/
var reverseBetween = function (head, left, right) {
let leftNode, rightNode, pre, succ;
const dummyHead = new ListNode(null, head);
pre = dummyHead;
for (let i = 0; i < left - 1; i++) {
pre = pre.next;
}
rightNode = pre;
for (let i = 0; i < right - left + 1; i++) {
rightNode = rightNode.next;
}
succ = rightNode.next;
leftNode = pre.next;
pre.next = null;
rightNode.next = null;
reverseList(leftNode);
function reverseList(head) {
let prev = null;
let cur = head;
while (cur) {
const tmp = cur.next;
cur.next = prev;
prev = cur;
cur = tmp;
}
}
pre.next = rightNode;
leftNode.next = succ;
return dummyHead.next;
};
复杂度
时间:O(N), 最多两次遍历全体链表,如果需要反转的部分接近了整体长度
空间:O(1),只需要保存几个指针