携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第10天,点击查看活动详情
LeetCode 92:反转链表 II
题目描述
给你单链表的头指针 head 和两个整数 left 和 right ,其中 left <= right 。请你反转从位置 left 到位置 right 的链表节点,返回 反转后的链表 。
解题思路
思路一:迭代
使用LeedCode 206:反转链表 - 掘金 (juejin.cn) 的解法,
-
先将
left到right部分(待反转的区域)反转; -
我们还需要记录
left的前一个节点prev2,和right的后一个节点curr2, 把prev2的next指针指向反转以后的链表头节点,把反转以后的链表的尾节点的next指针指向 curr
如图所示:
需要注意的点:
left为1,从头反转
实现代码如下:
var reverseBetween = function (head, left, right) {
let prev = null,
curr = head,
next = null;
for (let i = 1; i < left; i++) {
prev = curr;
curr = curr.next;
}
let prev2 = prev,
curr2 = curr;
// 反转left、right之间的链表
for (let i = left; i <= right; i++) {
next = curr.next;
curr.next = prev;
prev = curr;
curr = next;
// ES6的解构
// [prev.next, prev, curr] = [prev, curr, curr.next]
}
// 将反转后的链表与原链表拼接
// 将 pre2 的 next 指针指向反转后的链表头节点
// 考虑left == 1的情况
if (prev2 != null) {
prev2.next = prev;
} else {
head = prev;
}
// 将反转后的链表的尾节点的 next 指针指向 curr
curr2.next = curr;
return head;
};
时间复杂度: O(n) ,其中 n 是链表总节点数。最坏情况下,需要遍历整个链表2次
空间复杂度: O(1);
思路二:一次遍历,头插法
先来看看方法一的缺点:如果 left 和 right 恰好是链表的头节点和尾节点时,找到 left 和 right 需要遍历一次,反转它们之间的链表还需要遍历一次,虽然总的时间复杂度为 O(n),但遍历了链表 2 次,下一步优化为只遍历1次。
额外使用一个dummy node(哑节点)指向链表的头节点。在需要反转的区间里,每遍历到一个节点,让这个新节点来到反转部分的起始位置。
步骤:
先将 curr 的下一个节点记录为 next;
把 curr 的下一个节点指向 next 的下一个节点;
把 next 的下一个节点指向 pre 的下一个节点;
把 pre 的下一个节点指向 next。
其中curr指向待反转区域的第一个节点 left
解题代码如下:
var reverseBetween = function(head, left, right) {
let dummy = new ListNode(-1);
dummy.next = head;
let pre = dummy;
for (let i = 0; i < left - 1; ++i) {
pre = pre.next;
}
let curr = pre.next, next = null;
for (let i = 0; i < right - left; ++i) {
next = curr.next;
curr.next = next.next;
next.next = pre.next;
pre.next = next;
}
return dummy.next;
};
时间复杂度: O(n) ,其中 n 是链表总节点数。最多只遍历了链表一次
空间复杂度: O(1);