合并K个升序链表
0. 题目
给你一个链表数组,每个链表都已经按升序排列。
请你将所有链表合并到一个升序链表中,返回合并后的链表。
示例 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
1. 优先队列
1.1 分析
优先队列的主要思路:
- 利用优先队列的特性,把链表数组中所有的链表节点逐个放入优先队列;
- 然后逐个从优先队列poll出来,优先队列最小的最先出来;
1.2 代码 6ms 40.2mb
public ListNode mergeKLists(ListNode[] lists) {
if (lists == null || lists.length == 0) return null;
PriorityQueue<ListNode> queue = new PriorityQueue<>(new Comparator<ListNode>() {
public int compare(ListNode o1, ListNode o2) {
return o1.val - o2.val;
}
});
for (ListNode nodes : lists) {
while (nodes != null) {
queue.add(nodes);
nodes = nodes.next;
}
}
ListNode pre = new ListNode(0);
ListNode cur = pre;
while (!queue.isEmpty()) {
cur.next = queue.poll();
cur = cur.next;
}
cur.next = null;
return pre.next;
}
2. 优化的优先队列
2.1 分析
优化后的主要思路:
- 因为链表已经是升序的,所以最小的节点肯定从k个链表的头结点里面选;
- 优先队列节点个数可以始终保持是k个节点;
2.2 代码 5ms 40.2mb
public ListNode mergeKLists2(ListNode[] lists) {
if (lists == null || lists.length == 0) return null;
PriorityQueue<ListNode> queue = new PriorityQueue<>(new Comparator<ListNode>() {
public int compare(ListNode o1, ListNode o2) {
return o1.val - o2.val;
}
});
for (ListNode nodes : lists) {
if (nodes != null) {
queue.add(nodes);
}
}
ListNode pre = new ListNode(0);
ListNode cur = pre;
while (!queue.isEmpty()) {
ListNode temp = queue.poll();
cur.next = temp;
cur = cur.next;
if (temp.next != null) {
queue.add(temp.next);
}
}
cur.next = null;
return pre.next;
}
3. 两两合并
3.1 分析
主要思路:
- 采用分而治之的思想两两合并,最后合成一个链表;
- 链表数组从中间开始分割,直到两个链表为最小单元开始合并;
- 然后逐步合并已经被合并的链表
备注:此处也可以采用第一个链表与第二个链表合并,之后与第三个链表合并,之后与第四个链表合并,直到最后;这种运行时间较长达到200多毫秒。
3.2 代码 3ms 40.9MB
public ListNode mergeKLists3 (ListNode[] lists) {
if (lists == null | lists.length == 0) return null;
return merge(lists, 0, lists.length-1);
}
ListNode merge(ListNode[] lists, int begin, int end) {
if (begin == end) return lists[begin];
int middle = begin + (end - begin)/2;
ListNode left = merge(lists, begin, middle);
ListNode right = merge(lists, middle+1, end);
return mergeTwoList(left, right);
}
// 两个链表合并
ListNode mergeTwoList(ListNode l1, ListNode l2) {
if (l1 == null || l2 == null) {
return l1 == null ? l2 : l1;
}
if (l1.val <= l2.val) {
l1.next = mergeTwoList(l1.next, l2);
return l1;
} else {
l2.next = mergeTwoList(l1, l2.next);
return l2;
}