一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第28天,点击查看活动详情
题目(Sort List)
链接:https://leetcode-cn.com/problems/sort-list
解决数:1661
通过率:66.5%
标签:链表 双指针 分治 排序 归并排序
相关公司:bytedance amazon microsoft
给你链表的头结点 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) 时间复杂度和常数级空间复杂度下,对链表进行排序吗?
思路
解法一:归并排序 - 递归
- 摘抄讲解如下
- 1、把长度为n的输入序列分成两个长度为n/2的子序列
- 2、对这两个子序列分别采用归并排序
- 3、将两个排序好的子序列合并成一个最终的排序序列
- 对应链表
- 1、用双指针法(快慢指针)寻找链表中间节点
- 奇数个节点找到中点,偶数个节点找到中心左边的节点
- 注意
- 找到中点后,要将链表切断,即 mid.next = null
- 因链表性质,左边子序列取左端点即可
- 同数组归并一样,只剩一个节点时终止
- 用于分成左右两边子序列
- 右边子序列为慢指针的next
- 2、递归排序左右子序列
- 3、合并
- 同数组一样,判断值的大小
- 不同的是,用哨兵节点链接合并后的链表,返回即可
- 1、用双指针法(快慢指针)寻找链表中间节点
/**
* Definition for singly-linked list.
* function ListNode(val) {
* this.val = val;
* this.next = null;
* }
*/
/**
* @param {ListNode} head
* @return {ListNode}
*/
var sortList = function(head) {
let mergeList = (left,right) => {
let res = new ListNode(0);
let pre = res;
while(left && right){
if(left.val <= right.val){
pre.next = left;
left = left.next;
}else{
pre.next = right;
right = right.next;
}
pre = pre.next;
}
pre.next = left ? left : right;
return res.next;
}
let mergeSort = (node) => {
if(!node || !node.next) return node;
let mid = node;
let fast = node.next;
while(fast && fast.next){
mid = mid.next;
fast = fast.next.next;
}
let rightList = mid.next;
mid.next = null;
let left = node;
let right = rightList;
return mergeList(mergeSort(left),mergeSort(right));
}
return mergeSort(head);
};
解法二:归并排序 - 非递归
/**
* Definition for singly-linked list.
* function ListNode(val) {
* this.val = val;
* this.next = null;
* }
*/
/**
* @param {ListNode} head
* @return {ListNode}
*/
var sortList = function(head) {
// 哨兵节点
let preHead = new ListNode(0);
preHead.next = head;
// 求链表长度
let n = 0;
let curr = head;
while(curr){
curr = curr.next;
n++;
}
// 分割i长度的链表,返回剩余的链表
let split = (node,i) => {
while(i != 1 && node){
node = node.next;
i--;
}
let rest = node ? node.next : null;
if(node) node.next = null;
return rest;
}
// 合并
let merge = (left,right,pre) => {
let curr = pre;
while(left && right){
if(left.val <= right.val){
curr.next = left;
left = left.next;
}else{
curr.next = right;
right = right.next;
}
curr = curr.next;
}
curr.next = left || right;
while(curr.next) curr = curr.next;
return curr;
}
// 合并 2*i 个
for(let i = 1;i < n;i *= 2){
let pre = preHead;
let curr = preHead.next;
// 分割左右两部分链表,并合并
while(curr){
let left = curr;
let right = split(left,i);
curr = split(right,i);
pre = merge(left,right,pre);
}
}
return preHead.next;
};