23. Merge k Sorted Lists
Merge k sorted linked lists and return it as one sorted list. Analyze and describe its complexity.
31/05/2017
今天又看了一下这个题,重温了一下PriorityQueue。
这题思路:
每次把PriorityQueue里最小的那个Node撸下来,然后把这个最小Node的next结点加入到PriorityQueue里去。PriorityQueue的意义在于能保证队首永远是最小的Node,后面的顺序每次add/offer都会变,无法保证是按顺序的(堆排序我还需要再复习一遍),但是我们也不需要后面是按顺序的,只需要min heap的堆顶元素就行了。
这题用了堆heap
的思想,用的是优先权队列实现的。
public ListNode mergeKLists(ListNode[] lists) {
if (lists == null || lists.length == 0) return null;
PriorityQueue<ListNode> queue = new PriorityQueue<>(lists.length, new Comparator<ListNode>() {
@Override
public int compare(ListNode o1, ListNode o2) {
//none - descending sort
return o1.val - o2.val;
}
});
for (ListNode list : lists) {
if (list != null)
queue.offer(list);
}
ListNode dummy = new ListNode(-1);
ListNode tail = dummy;
while (!queue.isEmpty()) {
tail.next = queue.poll();
tail = tail.next;
//把每个链表上的node全撸下来加入到queue,注意这个撸下来的过程可能不是连续的,可能先撸了第一个链表的next就去撸下一个链表了
if (tail.next != null) {
queue.offer(tail.next);
}
}
return dummy.next;
}
一开始我不懂为什么这题的参数是一个数组,我以为应该是好几个参数,每个参数是一个list呢。直到看到
if (tail.next != null) {
queue.offer(tail.next);
}
我才恍然大悟,每个listNode自身就是一个链表呀。
引用CodeGanker的讲解:
维护一个大小为k的堆,每次取堆顶的最小元素放到结果中,然后读取该元素的下一个元素放入堆中,重新维护好。因为每个链表是有序的,每次又是去当前k个元素中最小的,所以当所有链表都读完时结束,这个时候所有元素按从小到大放在结果链表中。
其实我有点不太懂为什么这k个list要是已经排序好的了,因为这样看来根本不需要啊(我不懂Java的PriorityQueue是不是每次add/offer一个元素就会自动排序一次?如果是的话,那无论怎么插入元素,poll出来的永远是排好序的queue中的最小元素呀。。)
对于复杂度:这个算法每个元素要读取一次,即是k*n次,然后每次读取元素要把新元素插入堆中要logk的复杂度,所以总时间复杂度是O(nklogk)。空间复杂度是堆的大小,即为O(k)。