问题描述
- Merge k sorted linked lists and return it as one sorted list. Analyze and describe its complexity.
- Example:
Input:
[
1->4->5,
1->3->4,
2->6
]
Output: 1->1->2->3->4->4->5->6
问题分析
- 该题是 LeetCode 21. Merge Two Sorted Lists 的进阶题目。以下介绍的两种方法也是基于 21 合并的方法提出的。
-
堆
- 合并两条有序链表时,用的是双指针法,如果一个找到了,该指针右移。所以可以用一种类似于 k指针的方法,先将 k条链表的头部添加进一个小根堆,每次将堆顶元素出堆,然后将该出堆元素的下一个节点入堆。这样利用最小堆每次都能得到最小值的特点,便实现了一种k指针的作用。
-
分治法
- 类似于归并排序:
- 分:将前 k/2 条链表合并成一条有序链表 p1,将后 k/2条链表合并成一条有序链表 p2。
- 合:将有序链表p1,p2合并
- 类似于归并排序:
- 两种方法都是 O(NlogK),如果N为所有节点的个数,但是分治法要比堆法更加快。在空间上来看,堆法是O(K),分治法只是O(log N)
经验教训
- 如果对一个数据集,涉及到不断的插入删除,而且要求实时更新最大值或者最小值,那么肯定是堆
代码实现
- 堆
public ListNode mergeKLists(ListNode[] lists) {
if (lists == null || lists.length == 0) {
return null;
}
//建立一个最小堆,根据节点的val进行排序
PriorityQueue<ListNode> minHeap = new PriorityQueue<>(new Comparator<ListNode>(){
public int compare(ListNode o1, ListNode o2) {
return o1.val - o2.val;
}
});
ListNode dummy = new ListNode(0);
ListNode tail = dummy;
//初始化将k条链表的头结点(若不为空)入堆
for (int i = 0; i < lists.length; i++) {
if (lists[i] != null) {
minHeap.add(lists[i]);
}
}
//每次都将堆顶出堆,然后将出堆元素的下一个节点(如果存在)入堆
while(! minHeap.isEmpty()) {
ListNode curNode = minHeap.poll();
if (curNode.next != null) {
minHeap.add(curNode.next);
}
tail.next = curNode;
tail = tail.next;
}
return dummy.next;
}
- 分治法
public ListNode mergeKLists(ListNode[] lists) {
if (lists == null || lists.length == 0) {
return null;
}
return mergeKLists(lists, 0, lists.length - 1);
}
//将lists[begin...end]合并成一条有序链表
public ListNode mergeKLists(ListNode[] lists, int begin, int end) {
//base case
if (begin == end) {
return lists[begin];
}
int mid = begin + (end - begin) / 2;
// 对 begin ~ mid 进行合并成 p1
ListNode p1 = mergeKLists(lists, begin, mid);
// 对 mid + 1 ~ end 进行合并成 p2
ListNode p2 = mergeKLists(lists, mid + 1, end);
//p1 p2 合并
return mergeSortedList(p1, p2);
}
//合并两条有序链表
public ListNode mergeSortedList(ListNode l1, ListNode l2) {
if (l1 == null) {
return l2;
}
if (l2 == null) {
return l1;
}
ListNode dummy = new ListNode(0);
ListNode tail = dummy;
while (l1 != null && l2 != null) {
if (l1.val > l2.val) {
tail.next = l2;
l2 = l2.next;
}else {
tail.next = l1;
l1 = l1.next;
}
tail = tail.next;
}
tail.next = l1 == null ? l2 : l1;
return dummy.next;
}