实现思路
方法1.1
1 重点是需要明白,1次循环内实现 和 2轮循环单独处理的区别
- 1次循环内:此时虽然p1.next 被改变了,但是没有破坏p2.next的引用关系,所以还能正确移动p2
- 分开2次循环:在1次循环后,相当于原链表此时已经变成了 原来奇数位置组成的链表, 这时 p2.next的引用关系已被 彻底破坏,所以是不正确的
2 p1.next && p2.next能同时处理 奇数和偶数个数的情况
-
总数是奇数个:p2.next不会是null, 但是在进入循环执行后,p1会指向最后一个奇数,且p1.next为null && p2指向null;
-
总数是偶数个:p2.next会指向null,不会进入循环
方法1.2
1 要理解交替移动的指针,其循环终止条件:偶数之后无下一个节点 && 最后一个奇数指向节点不能是null
参考文档
代码实现
1 方法1.1: 迭代- 间隔移动 时间复杂度: O(n) 空间复杂度: O(1)
function oddEvenList(head) {
if (!head || !head.next) return head;
let p1Head = head, p2Head = head.next;
let p1 = head, p2 = head.next;
// 易错点:需要明白在1轮循环 和 2轮循环的区别
// 能同时支持奇数和偶数的情况:
// - 总数是奇数个:p2.next不会是null
// 但是在进入循环执行后,p1会指向最后一个奇数,且p1.next为null && p2指向null;
// - 总数是偶数个:p2.next会指向null,不会进入循环
while (p1.next && p2.next) {
p1.next = p1.next.next;
p2.next = p2.next.next
p1 = p1.next
p2 = p2.next
}
p1.next = p2Head;
return p1Head;
}
2 方法1.2: 迭代- 交替移动 时间复杂度: O(n) 空间复杂度: O(1)
function oddEvenList(head) {
if (!head || !head.next) return head;
let p2Head = head.next
let p1 = head, p2 = head.next;
// p2不为null 保证了奇数个数 正确终止的情况
// p2.next不为null 保证了偶数个数时 p1不会错误指向最后1个奇数的 后一位null的情况
while (p2 && p2.next) {
p1.next = p2.next
p1 = p1.next
p2.next = p1.next
p2 = p2.next
}
p1.next = p2Head;
return head;
}
3 方法2: 递归实现 时间复杂度: O(n); 空间复杂度(n)
let oddTail = null
function oddEvenList(head: ListNode | null): ListNode | null {
if (!head || !head.next) return head
// S1 子问题
let [, evenHead] = sortOddAndEven(head, head.next)
// S2 本轮操作:让奇数尾节点指向分类好的偶数头节点
oddTail.next = evenHead
// S3 返回分类连接的头节点
return head
};
// odd-->odd; even-->even; 返回分类好的 [oddHead, evevHead]
function sortOddAndEven(odd, even) {
if (!odd.next || !even.next) {
// 易错点:防止odd后还有一个偶数位置指向
odd.next = null
oddTail = odd
return [odd, even]
}
let [newOddHead, newEvenHead] = sortOddAndEven(odd.next.next, even.next.next)
odd.next = newOddHead
even.next = newEvenHead
return [odd, even]
}