大家好,我是挨打的阿木木,爱好算法的前端摸鱼老。最近会频繁给大家分享我刷算法题过程中的思路和心得。如果你也是想提高逼格的摸鱼老,欢迎关注我,一起学习。
题目
148. 排序链表
给你链表的头结点 head ,请将其按 升序 排列并返回 排序后的链表 。
示例 1:
输入: head = [4,2,1,3]
输出: [1,2,3,4]
示例 2:
输入: head = [-1,5,3,4,0]
输出: [-1,0,3,4,5]
示例 3:
输入: head = []
输出: []
提示:
- 链表中节点的数目在范围
[0, 5 * 104]内 -105 <= Node.val <= 105
进阶: 你可以在 O(n log n) 时间复杂度和常数级空间复杂度下,对链表进行排序吗?
堆排序
思路
- 使用堆排序,一个一个放到最小堆中去;记得链表放进去的时候要先切断跟下个节点的联系;
- 从堆中一个一个弹出最小元素,构建新的链表,直到全部弹出来为止。
实现
/**
* 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 {ListNode}
*/
var sortList = function(head) {
let minHeap = new MinPriorityQueue();
// 先全部放到最小堆中去
while (head) {
const next = head.next;
head.next = null;
minHeap.enqueue(head, head.val);
head = next;
}
let result = new ListNode(0);
let prev = result;
// 再从堆中一个个取出构建链表
while (minHeap.size()) {
prev.next = minHeap.dequeue().element;
prev = prev.next;
}
return result.next;
};
归并排序
思路
让相邻的链表片段两两进行有序合并,每个片段的长度为1 => 2 => 4 => 8 => ... => n
-
一轮遍历,先统计数量,同时切断每个节点的联系,把每个节点作为一个片段存放到数组中去;
-
相邻的片段两两合并,用合并有序链表的方法:
2.1 判断两个链表的头节点哪个比较小就哪个先放进结果中去,同时链表和结果都往后走一步;
2.2 直到一根链表走完了,把剩余部分接到最后面即可。
-
直到合并到数组中只有一个元素为止。
实现
/**
* 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 {ListNode}
*/
var sortList = function(head) {
if (!head) return null;
let cur = head;
let count = 0;
// 把每个小段都存在这里,下次拿的时候方便
let result = [];
// 一轮遍历,先统计数量,同时切断每个节点的联系
while (cur) {
const next = cur.next;
cur.next = null;
result.push(cur);
cur = next;
}
// 当有多个片段时候继续合并
while (result.length > 1) {
// 那么长度就会减少一半
let len = Math.ceil(result.length / 2);
// 遍历数组,两两合并
for (let i = 0; i < len; i++) {
result[i] = merge(result[i * 2], result[i * 2 + 1]);
}
// 切割掉无用的部分
result.length = len;
}
return result[0];
};
// 合并两个有序列表
function merge(list1, list2) {
let result = new ListNode(0);
let prev = result;
while (list1 && list2) {
if (list1.val <= list2.val) {
prev.next = list1;
list1 = list1.next;
} else {
prev.next = list2;
list2 = list2.next;
}
prev = prev.next;
}
// 剩余部分接上
prev.next = list1 || list2;
return result.next;
}
看懂了的小伙伴可以点个关注、咱们下道题目见。如无意外以后文章都会以这种形式,有好的建议欢迎评论区留言。