大家好,我是挨打的阿木木,爱好算法的前端摸鱼老。最近会频繁给大家分享我刷算法题过程中的思路和心得。如果你也是想提高逼格的摸鱼老,欢迎关注我,一起学习。
题目
143. 重排链表
给定一个单链表 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
思路
首先,如果不明白怎么翻转一条链表的,可以先从入门看起 [路飞]_送给前端开发者的链表入门教程
- 如果是普通的数组,咱们可以直接采取双指针,左一个,右一个,左一个,右一个...,这道题目就太简单了哈哈;
- 由于是链表,我们要拿到最后一个节点,所以我们首先得经过一轮遍历,拿到翻转后的链表,同时计算链表的长度
count
; - 这样子我们有了一条正序排列的链表和一条反序排列的链表了,每一个从正的取一个,反的取一个,就可以实现我们刚刚的左一个,右一个,左一个,右一个了;
- 如果链表的单数的,最后一轮要在左边在拿一个,然后记得切割掉链表最后一个节点的
next
的指向。
实现
/**
* 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) {
// 下一轮还需要头节点,找个代跑
let temp = head;
// 计算链表的长度
let count = 0;
// 生成一个倒序的链表
let reverseHead = null;
while (temp) {
let newNode = new ListNode(temp.val);
newNode.next = reverseHead;
reverseHead = newNode;
temp = temp.next;
count++;
}
let result = new ListNode(0);
let prev = result;
// 快乐的从左边拿一个,右边拿一个
while (count > 1) {
// 左一个
prev.next = head;
prev = prev.next;
head = head.next;
// 右一个
prev.next = reverseHead;
prev = prev.next;
reverseHead = reverseHead.next;
count -= 2;
}
// 单数的情况下,最后一轮只能左一个了
if (count === 1) {
// 左一个
prev.next = head;
prev = prev.next;
}
prev.next = null;
return result.next;
};
结果
翻车了。超出了输出限制,说明空间上用多了, 可以考虑从reverseHead
下手,我们其实用不到前半截,可以直接拿后半截来做一个翻转,也可以从result
下手,我们直接操作原链表,不用新开辟内存空间。这里我们选择用前者。
优化
通过快慢指针找到中点处,不用翻转整个链表,切断中点,翻转后半截链表即可,然后左右两边各取一个值进行操作。
优化代码
/**
* 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) {
// 通过快慢指针找到中点处的位置
let fast = head, slow = head;
let count = 0;
while (fast) {
fast = fast.next && fast.next.next;
slow = slow.next;
count++;
}
// 生成一个倒序的链表
let reverseHead = null;
while (slow) {
let newNode = new ListNode(slow.val);
newNode.next = reverseHead;
reverseHead = newNode;
slow = slow.next;
}
let result = new ListNode(0);
let prev = result;
while (count) {
// 左一个
prev.next = head;
prev = prev.next;
head = head.next;
// 右一个
prev.next = reverseHead;
prev = prev.next;
reverseHead = reverseHead && reverseHead.next;
count--;
}
return result.next;
};
看懂了的小伙伴可以点个关注、咱们下道题目见。如无意外以后文章都会以这种形式,有好的建议欢迎评论区留言。