探索字节跳动-合并K个升序链表|刷题打卡

148 阅读2分钟

本文正在参与掘金团队号上线活动,点击 查看大厂春招职位

一、题目描述:

给你一个链表数组,每个链表都已经按升序排列。

请你将所有链表合并到一个升序链表中,返回合并后的链表。

输入:lists = [[1,4,5],[1,3,4],[2,6]]
输出:[1,1,2,3,4,4,5,6]
解释:链表数组如下:
[
  1->4->5,
  1->3->4,
  2->6
]
将它们合并到一个有序链表中得到。
1->1->2->3->4->4->5->6

二、思路分析:

本道题的前置题目:合并两个升序链表其实是很简单的,只要比较两个链表的当前节点谁小就可以了,但是要合并K个就不太容易了,或者说写个时间复杂度不太糟糕的算法不太容易,主要问题是比较这K个链表的当前节点用什么方法比较合适,想了一下,想到的方法要么慢,要么不太容易优雅地实现,果断去瞄了眼讨论,果然大多数讨论是关于优先队列的,优先队列其实是堆的一个特例,然而平时不用,想不到,而js中没有现成的优先队列与堆的实现导致js解法还是以递归分治为主。以下是官方题解示意图:

image.png

综合考虑我还是觉得优先队列的方法最优雅,可能是由于我对于堆的偏爱吧。但是由于额外实现优先队列篇幅太长,贴个分治的AC解法

三、AC代码:

var mergeKLists = function(lists) {
    let n = lists.length;
    if(n == 0) return null;
    let mergeTwoLists = (l1,l2) => {
        if(l1 == null) return l2;
        if(l2 == null) return l1;
        if(l1.val <= l2.val){
            l1.next = mergeTwoLists(l1.next,l2);
            return l1;
        }else{
            l2.next = mergeTwoLists(l1,l2.next);
            return l2;
        }
    }
    let merge = (left,right) => {
        if(left == right) return lists[left];
        let mid = (left + right) >> 1;
        let l1 = merge(left,mid);
        let l2 = merge(mid+1,right);
        return mergeTwoLists(l1,l2);
    }
    return merge(0,n-1);
};

四、总结:

贴个别人的题解

var mergeKLists = function(lists) {
    return lists.reduce((p, n) => {
        while (n) {
            p.push(n), n = n.next
        }
        return p
    },[]).sort((a, b) => a.val - b.val).reduceRight((p, n) => (n.next = p, p = n, p), null)
};