给你一个链表数组,每个链表都已经按升序排列。
请你将所有链表合并到一个升序链表中,返回合并后的链表。
示例 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
来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/merge-k-sorted-lists/
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
感谢labuladong的单链表的六大解题套路,你都见过么?,王尼玛的四种解法+多图演示 23. 合并K个排序链表
class Solution {
public ListNode mergeKLists(ListNode[] lists) {
// return mergeKListsI(lists);
// return mergeKListsII(lists);
// return mergeKListsIII(lists);
return mergeKListsIV(lists);
}
//方法四:类似于四数之和,将大问题转换为熟悉的小问题
//时间复杂度O(n*k^2),空间复杂度O(logk)
//四数之和:https://blog.csdn.net/Bonbon_wen/article/details/111501252
private ListNode mergeKListsIV(ListNode[] lists) {
if (lists == null || lists.length == 0) {
return null;
}
int k = lists.length;
if (k == 1) {
return lists[0];
}
return mergeHelper(lists, 0, k);
}
private ListNode mergeHelper(ListNode[] lists, int start, int k) {
int len = lists.length;
ListNode res = null;
if (k == 2) {
return mergeTwoList(lists[start], lists[len - 1]);
}
for (int i = start; i < len - (k - 1); i++) {
ListNode sub = mergeHelper(lists, start + 1, k - 1);
res = mergeTwoList(lists[start], sub);
}
return res;
}
//方法三:利用二叉堆,将链表头节点放入一个最小堆中,每次获取k个节点中最小节点
//时间复杂度O(n*logk),空间复杂度O(k)
private ListNode mergeKListsIII(ListNode[] lists) {
if (lists == null || lists.length == 0) {
return null;
}
ListNode dummy = new ListNode(-1);
ListNode p = dummy;
PriorityQueue<ListNode> pq = new PriorityQueue<>(lists.length, (a, b) -> a.val - b.val);
//将 k 个链表的头结点加入最小堆
for (ListNode head : lists) {
if (head != null) {
pq.offer(head);
}
}
while (!pq.isEmpty()) {
//获取最小节点,添加到结果链表中
ListNode node = pq.poll();
p.next = node;
if (node.next != null) {
pq.offer(node.next);
}
//p 指针不断前进
p = p.next;
}
return dummy.next;
}
//方法二:分治算法,两两合并
//将数组一分为二,然后再拆分,直到规模为1不能再拆分时便返回,之后再两两合并。然后不断重复这个合并过程,直到最终得到一个有序的链表。
//时间复杂度O(n*logk),空间复杂度O(logk)
private ListNode mergeKListsII(ListNode[] lists) {
if (lists == null || lists.length == 0) {
return null;
}
return mergeLists(lists, 0, lists.length - 1);
}
private ListNode mergeLists(ListNode[] lists, int start, int end) {
if (start > end) {
return null;
}
if (start == end) {
return lists[start];
}
//通过mid将数组一分为二,并不断缩小规模,当规模为1时返回并开始合并
int mid = start + (end - start) / 2;
ListNode left = mergeLists(lists, start, mid);
ListNode right = mergeLists(lists, mid + 1, end);
//通过合并两个链表,不断增大其规模,整体看就是不断缩小 --> 最后不断扩大的过程
return mergeTwoList(left, right);
}
//方法一:逐一合并两条链表
//利用第21题思想,用一个变量ans来维护合并的链表,循环调用两两合并链表的方法
//时间复杂度O(n*k^2),空间复杂度O(1)
private ListNode mergeKListsI(ListNode[] lists) {
if (lists == null || lists.length == 0) {
return null;
}
ListNode ans = null;
for (ListNode list : lists) {
ans = mergeTwoList(ans, list);
}
return ans;
}
private ListNode mergeTwoList(ListNode l1, ListNode l2) {
ListNode dummy = new ListNode(-1);
ListNode prev = dummy;
while (l1 != null && l2 != null) {
if (l1.val <= l2.val) {
prev.next = l1;
l1 = l1.next;
} else {
prev.next = l2;
l2 = l2.next;
}
prev = prev.next;
}
if (l1 == null) {
prev.next = l2;
}
if (l2 == null) {
prev.next = l1;
}
return dummy.next;
}
}