实现思路
方法1.1 迭代- 反转链表节点- 标准反转方法
1 反转链表递归标准实现- 图示法
方法1.2 迭代- 反转链表节点- 头插法
1 设置锚点 和 当前待移动节点的 前一个节点
2 不断把 当前待移动节点的移动到锚点之后,从而形成反转
方法2.2- 递归- 转化为reverseTopN
1 关键是要逐步缩小问题规模,从reverseBetween 转化为 reverseTopN
2 left 其实是指相对链表头偏移了多少个节点; right指的是 相对left有多少个节点需要被反转
参考文档
代码实现
方法1.1- 迭代- 标准反转链表节点方法 时间复杂度: O(n) 空间复杂度: O(1)
function reverseBetween(head, left, right) {
let dummy = new ListNode(-1, head)
// p0固定指向 反转区间的前一个节点
let p0 = dummy
for (let i = 0; i < left - 1; i++) {
p0 = p0.next
}
// 对反转区间进行 标准节点反转
let pre = null, cur = p0.next
for (let i = 0; i < right - left + 1; i++) {
let willNext = cur.next
cur.next = pre
pre = cur
cur = willNext
}
// 执行到此时,pre是反转区间的新头节点;cur是反转区间之后的那个节点,即
// 原链表: 1 --> 2 --> 3 --> 4 --> 5
// 反转了2~4之间的链表:1 --> 2 <-- 3 <-- 4 --> 5
// |
// null
// p0 pre cur
// 期望的最终结果链表: 1 --> 4 --> 3 --> 2 --> 5
// 因此,接下来这2步,就是 很自然的连接 新的首尾节点即可
p0.next.next = cur
p0.next = pre
return dummy.next
}
方法1.2- 迭代- 头插法 时间复杂度: O(n) 空间复杂度: O(1)
function reverseBetween(head: ListNode | null, left: number, right: number): ListNode | null {
if (!head || !head.next) return head;
let dummy = new ListNode(-1, head)
let p0 = dummy, cur = dummy.next
for (let i = 0; i < left - 1; i++) {
p0 = p0.next
cur = cur.next
}
for (let i = 0; i < right - left; i++) {
let willMove = cur.next
cur.next = willMove.next
// 易错点:此时应该让待移动的头插节点,移动到最前面,也就是p0之后
willMove.next = p0.next
p0.next = willMove
}
return dummy.next
};
方法2.1- 递归- depth判断法 时间复杂度: O(n) 空间复杂度: O(n)
function reverseBetween(head: ListNode | null, left: number, right: number): ListNode | null {
if (!head || !head.next) return head;
if (left === right) return head;
return reverse(head, left, right, 1)[0];
};
function reverse(head, left, right, depth) {
if (depth === right) {
const tail = head.next;
// 易错点1: 新的头节点 head.next不能是null
// 而是要指向自己,这样才能保证后续反转节点 不会是空指针
head.next = head;
return [head, tail];
}
const [newHead, newTail] = reverse(head.next, left, right, depth + 1);
if (depth <= left - 1) {
head.next = newHead;
// 易错点2: 要保证递归的返回格式是统一的
return [ head ];
} else if (depth >= left) {
// 反转区间的情况:进行节点反转
head.next.next = head;
head.next = depth === left ? newTail : null;
return [newHead, newTail];
}
}
方法2.2- 递归- 转化为reverseTopN 时间复杂度: O(n); 空间复杂度(n)
let successor = null
function reverseBetween(head: ListNode | null, left: number, right: number): ListNode | null {
//S3 递归终止条件:相当于是 反转了链表的前n个节点,返回最新的头节点
if (left === 1) {
return reverseTopN(head, right)
}
//S1 递归含义:反转一个链表的部分节点,返回反转后的新头节点
let newHead = reverseBetween(head.next, left-1, right-1)
//S2.1 本轮操作:反转了部分节点后,让当前节点连接已经反转好的新链表头即可
head.next = newHead
//S2.2 返回拼接了本轮head的全规模链表,就是最终结果
return head
};
// 反转了链表的前n个节点,返回最新的头节点
function reverseTopN(head: ListNode, n: number) {
// S3 处理递归中止条件
if (n === 1) {
successor = head.next
return head
}
//S1 缩小为 子规模
let newHead = reverseTopN(head.next, n-1)
//S2 本轮数据处理
head.next.next = head
head.next = successor
return newHead
}