LeetCode_23_困难_合并 K 个升序链表


1. 题目

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

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

示例 1:

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

示例 2:

输入: l i s t s = [ ] lists = [] lists=[]
输出: [ ] [] []

示例 3:

输入: l i s t s = [ [ ] ] lists = [[ ]] lists=[[]]
输出: [ ] [ ] []


提示

  • k = = l i s t s . l e n g t h k == lists.length k==lists.length
  • 0 < = k < = 1 0 4 0 <= k <= 10^4 0<=k<=104
  • 0 < = l i s t s [ i ] . l e n g t h < = 500 0 <= lists[i].length <= 500 0<=lists[i].length<=500
  • − 1 0 4 < = l i s t s [ i ] [ j ] < = 1 0 4 -10^4 <= lists[i][j] <= 10^4 104<=lists[i][j]<=104
  • l i s t s [ i ] lists[i] lists[i]升序 排列
  • l i s t s [ i ] . l e n g t h lists[i].length lists[i].length 的总和不超过 1 0 4 10^4 104

2. 思路及代码实现(Python)

对于这种数组排序的问题,有个直接的思路是把链表的节点值取出进行数组排序,再生成链表,性能非常好(数组排序做了很多优化),但为了体现算法逻辑,一般采取一些比较能体现算法思维的求解方法。

在之前有道题目与本题相似,就是《LeetCode_21_简单_合并两个有序链表》,当中有一个迭代的方法,设置一个哨兵节点,在每次迭代的时候把指针指向两个列表中较小的节点,直到某一链表指向空,此时再把另一链表的剩余部分接到结果链表的结尾。

而对于多个升序链表的合并,可以用以下的思路:

题解来源:灵茶山艾府

2.1 最小堆

对于多个升序链表,同样的可以设立一个哨兵节点 dummy不断指向链表地最小值,这里的题解思路用的是最小堆,且对最小堆的大小函数进行重写,当然,也可以用其他的数据结构,但要能够实现结果链表指向下一个节点的同时,该节点所属的链表的头节点也向后更新。

如下对 L i s t N o d e ListNode ListNode 的魔术方法 __lt__ 进行重写,该方法用在比大小时调用,重写该方法,即使得 L i s t N o d e ListNode ListNode 对象能够直接根据 v a l val val 属性值比大小。该算法的时间复杂度为 O ( n l o g k ) O(nlogk) O(nlogk),空间复杂度为 O ( k ) O(k) O(k)

ListNode.__lt__ = lambda a, b: a.val < b.val  # 让堆可以比较节点大小

class Solution:
    def mergeKLists(self, lists: List[Optional[ListNode]]) -> Optional[ListNode]:
        cur = dummy = ListNode()  # 哨兵节点,作为合并后链表头节点的前一个节点
        h = [head for head in lists if head]  # 初始把所有链表的头节点入堆
        heapify(h)  # 堆化
        while h:  # 循环直到堆为空
            node = heappop(h)  # 剩余节点中的最小节点
            if node.next:  # 下一个节点不为空
                heappush(h, node.next)  # 下一个节点有可能是最小节点,入堆
            cur.next = node  # 合并到新链表中
            cur = cur.next  # 准备合并下一个节点
        return dummy.next  # 哨兵节点的下一个节点就是新链表的头节点

执行用时:67 ms
消耗内存:18.88 MB

  • 31
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值