「前端刷题」148.排序链表(MEDIUM)

112 阅读1分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 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、合并
      • 同数组一样,判断值的大小
      • 不同的是,用哨兵节点链接合并后的链表,返回即可
/**
 * 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;
};