持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第1天,点击查看活动详情
每日刷题 2022.09.28
- leetcode原题链接:leetcode.cn/problems/LG…
题目
- 给定一个单链表 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 * 10^4] 1 <= node.val <= 1000
解题思路
- 根据题意分析:需要交替重新拼接节点,那么就需要知道节点的中点在哪里。
- 使用快慢指针,找到链表的中点,并记录下来当前的整个链表的长度;
- 使用数组存放链表节点指针,同时将每个节点的next设置为null;
- 先取出首位位置接入到链表中,用node作为尾部指针;
- 使用l,r指向数组中剩余链表两侧,不断取出节点加入到链表尾部,并更新尾部指针,直到l和r相遇;
- 如果l和r相等,说明还剩余一个节点没有加入链表,将其加入链表尾部。
- 注意⚠️:当链表长度为1时,不需要执行剩下的操作。
回顾经典的做法
- 使用三个指针
pre、cur、nxt,创建一个新的虚拟头节点(最后输出结果的时候,将其跳过即可,例:head.next)。 - 每次拼接的时候,需要头节点的下一个指针,指向当前节点
cur,然后将nxt等于nxt = cur.next,再将当前节点的下一个指针节点置为null。最后将当前节点指向下一个节点。重复这样的循环进行操作,最终可以得到重排后的链表。
代码
/**
* @param {ListNode} head
* @return {void} Do not return anything, modify head in-place instead.
*/
var reorderList = function(head) {
// 解法: 先快慢指针,再反转后半段链表,之后将两个链表进行合并
// 快慢指针找到中点
let slow = head;
let fast = head;
// 记录中点的前一个节点
let pre = null;
// 记录链表的长度
let len = 1;
while (null != fast.next) {
if (fast.next.next != null) {
// 快指针跳2
fast = fast.next.next;
len += 2;
}else {
fast = fast.next;
len++;
}
// 慢指针跳1
pre = slow;
slow = slow.next;
}
// 循环结束后,fast指针为尾节点,slow指针为中间节点
if (len == 1) {
return head;
}
// 获取前半段链表
pre.next = null;
// slow指针记录了后半段的链表,现在将其进行反转reverse,使用三个指针
function reverse(slow) {
let prev = null;
let cur = slow;
let next = null;
while (cur) {
next = cur.next;
cur.next = prev;
prev = cur;
cur = next;
}
return prev;
}
slow = reverse(slow);
// 将两个链表进行合并
let ans = new ListNode();
ans.val = 111;
let ansH = ans;
while (head || slow) {
if (head) {
ans.next = head;
head = head.next;
ans = ans.next;
}
if (slow) {
ans.next = slow;
slow = slow.next;
ans = ans.next;
}
}
return ansH.next;
};