问题描述:
Merge k sorted linked lists and return it as one sorted list. Analyze and describe its complexity.
原问题链接:https://leetcode.com/problems/merge-k-sorted-lists/
问题分析
这个问题看起来比较复杂,但是结合我前面讨论heap sort和priority queue的文章分析的话,则会发现其实这是一个固定的套路。
这个问题的解决思路基本如下。首先用priority queue将整个list里面的每个链表的第一个元素都放入到里面。然后每次从queue里取它的第一个元素。按照priority queue的定义,它最前面的元素必然是最小的元素。然后再判断这个取出来的元素后面是否还有别的元素。有的话则将它后面的那个元素加入到priority queue中间。
因为要返回一个排序的链表,这里可以创建一个链表的节点,然后用一个节点每次指向从queue里取出的那个元素。然后再往后移动指针。
这个问题有一个取巧的地方。就是它只适用于链表的情况。因为这里每次取到了队列头部的元素可以顺便拿到它后面的元素。而如果是ArrayList之类的结构则不能得到这样的信息。问题就会要复杂很多。
总之,根据前面的讨论可以得到如下的代码:
/** * Definition for singly-linked list. * public class ListNode { * int val; * ListNode next; * ListNode(int x) { val = x; } * } */ public class Solution { public ListNode mergeKLists(ListNode[] lists) { if(lists == null || lists.length == 0) return null; if(lists.length == 1) return lists[0]; Queue<ListNode> queue = new PriorityQueue<>(lists.length, new Comparator<ListNode>() { @Override public int compare(ListNode l1, ListNode l2) { return l1.val - l2.val; }}); for(ListNode node : lists) { if(node != null) queue.add(node); } ListNode pre = new ListNode(0); ListNode head = pre; while(!queue.isEmpty()) { ListNode temp = queue.poll(); head.next = temp; head = head.next; if(temp.next != null) queue.add(temp.next); } return pre.next; } }
假设list的长度为k的话,上述代码实现的时间复杂度为O(logK * N)。
总结
这个问题和前面的priority queue,堆排序它们有密切的关系。里面的各种推导和变换有不少值得深究的地方。