“Offer 驾到,掘友接招!我正在参与2022春招打卡活动,点击查看活动详情。”
给定一个单链表 L **的头节点 head ,单链表 L 表示为:
L0 → L1 → … → Ln - 1 → Ln
请将其重新排列后变为:
L0 → Ln → L1 → Ln - 1 → L2 → Ln - 2 → …
不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。
示例 1:
输入:head = [1,2,3,4]
输出:[1,4,2,3]
示例 2:
输入:head = [1,2,3,4,5]
输出:[1,5,2,4,3]
提示:
- 链表的长度范围为
[1, 5 * 104] 1 <= node.val <= 1000
双指针(翻转 + 插入)
-
把链接分成两半:寻找中间节点,使用快慢指针,快指针一次走两步,慢指针一次走一步,当快指针走到链表尾节点时慢指针刚好走到链表的中间节点
- 当链表的节点总数是奇数时,要确保链表的前半段比后半段多一个节点
-
反转链表的后半段
-
将两个链表依次相链
在初始化方面
- slow 为第一个结点开始
- fast 为第二个结点开始
在分成两半时
- fast 走两步,不能一次性走两步,要一步一步走
- 对于 fast 第二步,需要先判断,如果 fast 后面还可以再走一步,才往后走
/**
* Definition for singly-linked list.
* function ListNode(val, next) {
* this.val = (val===undefined ? 0 : val)
* this.next = (next===undefined ? null : next)
* }
*/
/**
* @param {ListNode} head
* @return {void} Do not return anything, modify head in-place instead.
*/
var reorderList = function(head) {
if(!head.next) return head;
let slow = head, fast = head.next; // slow 为第一个结点开始,fast 为第二个结点开始,这样结点总个数奇数或偶数都很好兼容
// fast 走两步,不能一次性走两步,要一步一步走
while(fast && fast.next) {
slow = slow.next;
fast = fast.next;
// 对于 fast 第二步,需要先判断,如果 fast 后面还可以再走一步,才往后走
if(fast.next) {
fast = fast.next;
}
}
// 现在 slow 和 fast 之间就是要翻转的部分了 (slow, fast]
let newHead = null, p = slow.next, q;
slow.next = null; // 前半部分和后半部分断开联系
while(p) {
q = p;
p = p.next;
q.next = newHead;
newHead = q;
}
// 现在 newHead 就是后半段翻转后的样子,然后接下来跟前半部分 依次 插入新链表
let resultHead = new ListNode(0), now = resultHead, flag = true;
slow = head, fast = newHead;
while(slow || fast) {
// 通过 flag 控制现在插入 前半部分 还是 后半部分
if(flag) {
now.next = slow;
slow = slow.next;
} else {
now.next = fast;
fast = fast.next;
}
now = now.next;
flag = !flag;
}
return resultHead.next;
};