「这是我参与2022首次更文挑战的第26天,活动详情查看:2022首次更文挑战」。
题目
给你链表的头结点 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 = []
输出:[]
思路
最常见的思路如下:
- 将链表的每个节点放入数组
stack
中 - 然后使用对数组的每个元素,按照对应的
val
排序 - 遍历数组,将
stack[i].next = stack[i + 1] || null
- 返回数组的第一个元素,即为新链表的头节点
代码如下
/**
* 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 head;
// 链表节点放入数组中
const stack = [];
while (head) {
stack.push(head);
head = head.next;
}
// 排序
stack.sort((a, b) => a.val - b.val);
// 重新组合链表
for(let i = 0; i < stack.length; i++) {
stack[i].next = stack[i + 1] || null;
}
// 返回头节点
return stack[0]
};
上述解法由于调用了 sort
Api,在不同平台的时间复杂度,包括使用的算法都不同,稳定性较低
思考: 对于链表的排序,如果想要将时间复杂度降低,使用 归并排序
是比较好的方法
归并
的两个主要步骤:
-
1、
归
,利用二分法,每次以链表的中间节点为分割点,将链表分为两部分,分别进行排序;递归执行,直到链表的长度小于等于1 -
2、
并
,将两个有序链表进行合并(传送门 ☞
),返回头节点给上一步,层层向上,最后即可得出问题的答案
/**
* @param {ListNode} head
* @return {ListNode}
*/
var sortList = function(head) {
if(!head || !head.next) return head;
// 快慢指针
let slow = head, fast = head;
// 保存slow节点的前一个节点
let prevSlow = null;
// 快指针每次走两步,慢指针走一步
while(fast && fast.next) {
prevSlow = slow;
slow = slow.next;
fast = fast.next.next;
}
// 解除 slow 节点的前序节点的指向
prevSlow.next = null;
// 递归执行排序
const l1 = sortList(head);
const l2 = sortList(slow);
// 返回 合并后的链表
return merge(l1, l2);
};
function merge(l1, l2) {
if(!l1) return l2;
if(!l2) return l1;
const head = h = new ListNode(0);
while(l1 && l2) {
if(l1.val > l2.val) {
h.next = l2;
l2 = l2.next;
} else {
h.next = l1;
l1 = l1.next;
}
h = h.next;
}
if(l1) h.next = l1;
if(l2) h.next = l2;
// 返回头节点
return head.next;
}
小结
归并排序的时间复杂度可以降低为 O(nLogn)
,记住先 归
,将数据拆分入栈,最后将最小单元的数据重新组合,称为 并
,就能牢记住该算法的主要思想。
LeetCode 👉 HOT 100 👉 排序链表 - 中等题 ✅
合集:LeetCode 👉 HOT 100,有空就会更新,大家多多支持,点个赞👍
如果大家有好的解法,或者发现本文理解不对的地方,欢迎留言评论 😄