[前端]_一起刷leetcode 148. 排序链表

117 阅读3分钟

大家好,我是挨打的阿木木,爱好算法的前端摸鱼老。最近会频繁给大家分享我刷算法题过程中的思路和心得。如果你也是想提高逼格的摸鱼老,欢迎关注我,一起学习。

题目

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) 时间复杂度和常数级空间复杂度下,对链表进行排序吗?

堆排序

思路

  1. 使用堆排序,一个一个放到最小堆中去;记得链表放进去的时候要先切断跟下个节点的联系;
  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) {
    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

  1. 一轮遍历,先统计数量,同时切断每个节点的联系,把每个节点作为一个片段存放到数组中去;

  2. 相邻的片段两两合并,用合并有序链表的方法:

    2.1 判断两个链表的头节点哪个比较小就哪个先放进结果中去,同时链表和结果都往后走一步;

    2.2 直到一根链表走完了,把剩余部分接到最后面即可。

  3. 直到合并到数组中只有一个元素为止。

实现

/**
 * 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;
}

看懂了的小伙伴可以点个关注、咱们下道题目见。如无意外以后文章都会以这种形式,有好的建议欢迎评论区留言。