合并k个已经排好序的数列是面试中也比较容易被问到的一个算法,有很多种解决,其中第一时间比较容易想到的解法如下:
对于这三组从小到大的数列:
如此循环,最终就将三个已经排序的数列的数字按从小到大的顺序排序到最终结果集了,那先来看一下这种算法的时间复杂度:
如果总共有k个排序好的数列,而几个数列中的总数字为n个,每拿一个数都要进行k次的循环比较,很明显其时间复杂度T(n) = O(n*k)。
那如果面试官问你如何对它进行优化呢?也就是用一个效率更高的算法来代替它,这时就需要用到之前学过的最小堆去优化啦,因为每次最小堆的根结点就是该数列的最小值,所以说用最小堆去实现的话也就是将每个数列中待比较的数字都放到最小堆中,其中涉及到交换元素,最终拿根结点就可以拿到最小的值了,这样对于k个数列来说其时间复杂度也就是logk,比之前的要比较k次才能得到最小值效率要高,也就是利用最小堆实现合并排序数列的时间复杂度T(n) = O(n * logk),当k值越大时,其logk比k远远要小很多,当然如果K值比较小其优化力度体现不是特别明显,所以在回答面试官时需要强调:在K值比较多的情况下用最小堆去优化是一个比较好的方案。
所以接下来就是来看一下如何来实现这种优化的算法,这里采用JAVA来实现,因为实现起来也比较好理由,下面先来搭建框架:
import java.util.ArrayList; import java.util.Comparator; import java.util.PriorityQueue; public class MergeKList { //用链表来表达数列 static class ListNode { int value; ListNode next; ListNode(int value) { this.value = value; } } //开始合并有序序列 private void mergeKLists(ArrayList<ListNode> lists) { //TODO } public static void main(String[] args) { //构造三个有序数列 ListNode h1 = new ListNode(2); h1.next = new ListNode(5); h1.next.next = new ListNode(7); ListNode h2 = new ListNode(3); h2.next = new ListNode(9); h2.next.next = new ListNode(10); ListNode h3 = new ListNode(1); h3.next = new ListNode(4); ArrayList<ListNode> lists = new ArrayList<ListNode>(); lists.add(h1); lists.add(h2); lists.add(h3); new MergeKList().mergeKLists(lists); } }
接着就是实现核心的合并方法:mergeKLists(),这里需要涉及到优先队列:PriorityQueue:
其中它就是用堆来实现的,所以功能跟堆类似,将一堆数字添加到优先队列中,最终拿的话按一定的条件拿。对于Queue我们已经非常熟了,先进先出,也就是先进去的先出来,但是!!这个优先队列是按条件来出的,可以最小的元素先出来,有了这么一个现成的类那实现就显得异常简单啦,所以这也是为啥要采用java来实现的原因,具体如下:
import java.util.ArrayList; import java.util.Comparator; import java.util.PriorityQueue; public class MergeKList { //用链表来表达数列 static class ListNode { int value; ListNode next; ListNode(int value) { this.value = value; } } //开始合并有序序列 private void mergeKLists(ArrayList<ListNode> lists) { PriorityQueue<ListNode> q = new PriorityQueue<ListNode>(lists.size(), new Comparator<ListNode>(){ public int compare(ListNode a, ListNode b) { return a.value - b.value; } }); //将每个序列的数添加到优先队列中 for(ListNode list : lists) { q.add(list); } while(q.size() > 0) { ListNode temp = q.poll();//直接拿出最小的元素,由优先队列类来保证 System.out.println(temp.value); if(temp.next != null) q.add(temp.next);//如果当前序列有下一个元素则直接将它再加入到优先队列中 } } public static void main(String[] args) { //构造三个有序数列 ListNode h1 = new ListNode(2); h1.next = new ListNode(5); h1.next.next = new ListNode(7); ListNode h2 = new ListNode(3); h2.next = new ListNode(9); h2.next.next = new ListNode(10); ListNode h3 = new ListNode(1); h3.next = new ListNode(4); ArrayList<ListNode> lists = new ArrayList<ListNode>(); lists.add(h1); lists.add(h2); lists.add(h3); new MergeKList().mergeKLists(lists); } }
编译运行:
由于比较简单,所以这里就不debug啦~~