leetcode
23. 合并 K 个升序链表

问题描述

LeetCode 23. 合并 K 个升序链表 (opens in a new tab),难度困难

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

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

示例 1

输入: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

示例 2

输入:lists = []
输出:[]

示例 3

输入:lists = [[]]
输出:[]

提示:

  • k == lists.length
  • 0 <= k <= 10^4
  • 0 <= lists[i].length <= 500
  • -10^4 <= lists[i][j] <= 10^4
  • lists[i]升序 排列
  • lists[i].length 的总和不超过 10^4

题解

优先队列(小顶堆)

解题思路:拿到题首先想到以下几个方法

  • 暴力:将所有数据存入数组中再排序,最后转成链表;时间复杂度 O(NlogN)N 表示所有链表的节点总数),空间复杂度 O(N)
  • 两两合并:两两合并链表,最终将所有链表合并为一个;每次合并的时间复杂度是 O(N),总的时间复杂度是 O(KN)
  • 优先队列(小顶堆):使用小顶堆,每次将堆顶元素(当前最小节点)取出并放入结果链表中,然后将该节点的下一个节点放入堆中;时间复杂度 O(NlogK),其中 N 是所有链表节点的总数,空间复杂度 O(K)

优先队列实现思路:创建优先队列(小顶堆)来处理 K 个链表的合并。每次获取当前所有链表中最小的节点。

  • 我们需要使用优先队列存储每个链表的头节点。
  • 每次从优先队列中取出最小的节点,将其加入到结果链表中。
  • 如果取出的节点有下一个节点,则将下一个节点放入优先队列。
Solution.java
class Solution {
    public ListNode mergeKLists(ListNode[] lists) {
        // 定义小顶堆
        PriorityQueue<ListNode> pq = new PriorityQueue<>(new Comparator<ListNode>() {
            @Override
            public int compare(ListNode o1, ListNode o2) {
                return o1.val - o2.val;
            }
        });
        // 将 list 的表头都放进优先队列
        for (ListNode head : lists) {
            if (head != null) {
                pq.offer(head);
            }
        }
 
        ListNode result = new ListNode();
        ListNode temp = result;
        // 优先队列不为空的情况下
        while (!pq.isEmpty()) {
            // 从 pq 里面取出最小的节点 node 放入 temp 节点后面
            ListNode node = pq.poll();
            temp.next = node;
            temp = temp.next;
            // 将 node 节点的后续节点加入到 pq 里面
            if (node.next != null) {
                pq.offer(node.next);
            }
        }
        return result.next;
    }
}