Merge k Sorted Lists -- LeetCode

转自: http://blog.csdn.net/linhuanmars/article/details/19899259?utm_source=tuicool


原题链接: http://oj.leetcode.com/problems/merge-k-sorted-lists/ 

这道题目在分布式系统中非常常见,来自不同client的sorted list要在central server上面merge起来。这个题目一般有两种做法,下面一一介绍并且分析复杂度。 第一种做法比较容易想到,就是有点类似于MergeSort的思路,就是分治法,不了解MergeSort的朋友,请参见归并排序-维基百科,是一个比较经典的O(nlogn)的排序算法,还是比较重要的。思路是先分成两个子任务,然后递归求子任务,最后回溯回来。这个题目也是这样,先把k个list分成两半,然后继续划分,知道剩下两个list就合并起来,合并时会用到Merge Two Sorted Lists这道题,不熟悉的朋友可以复习一下。代码如下: 

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. public ListNode mergeKLists(ArrayList<ListNode> lists) {  
  2.     if(lists==null || lists.size()==0)  
  3.         return null;  
  4.     return helper(lists,0,lists.size()-1);  
  5. }  
  6. private ListNode helper(ArrayList<ListNode> lists, int l, int r)  
  7. {  
  8.     if(l<r)  
  9.     {  
  10.         int m = (l+r)/2;  
  11.         return merge(helper(lists,l,m),helper(lists,m+1,r));  
  12.     }  
  13.     return lists.get(l);  
  14. }  
  15. private ListNode merge(ListNode l1, ListNode l2)  
  16. {   
  17.     ListNode dummy = new ListNode(0);  
  18.     dummy.next = l1;  
  19.     ListNode cur = dummy;  
  20.     while(l1!=null && l2!=null)  
  21.     {  
  22.         if(l1.val<l2.val)  
  23.         {  
  24.             l1 = l1.next;  
  25.         }  
  26.         else  
  27.         {  
  28.             ListNode next = l2.next;  
  29.             cur.next = l2;  
  30.             l2.next = l1;  
  31.             l2 = next;  
  32.         }  
  33.         cur = cur.next;  
  34.     }  
  35.     if(l2!=null)  
  36.         cur.next = l2;  
  37.     return dummy.next;  
  38. }  
我们来分析一下上述算法的时间复杂度。假设总共有k个list,每个list的最大长度是n,那么运行时间满足递推式T(k) = 2T(k/2)+O(n*k)。根据主定理,可以算出算法的总复杂度是O(nklogk)。如果不了解主定理的朋友,可以参见 主定理-维基百科 。空间复杂度的话是递归栈的大小O(logk)。
接下来我们来看第二种方法。这种方法用到了堆的数据结构,思路比较难想到,但是其实原理比较简单。维护一个大小为k的堆,每次取堆顶的最小元素放到结果中,然后读取该元素的下一个元素放入堆中,重新维护好。因为每个链表是有序的,每次又是去当前k个元素中最小的,所以当所有链表都读完时结束,这个时候所有元素按从小到大放在结果链表中。这个算法每个元素要读取一次,即是k*n次,然后每次读取元素要把新元素插入堆中要logk的复杂度,所以总时间复杂度是O(nklogk)。空间复杂度是堆的大小,即为O(k)。代码如下:

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. public ListNode mergeKLists(ArrayList<ListNode> lists) {  
  2.     PriorityQueue<ListNode> heap = new PriorityQueue<ListNode>(10,new Comparator<ListNode>(){  
  3.             @Override  
  4.             public int compare(ListNode n1, ListNode n2)  
  5.             {  
  6.                 return n1.val-n2.val;  
  7.             }  
  8.         });  
  9.     for(int i=0;i<lists.size();i++)  
  10.     {  
  11.         ListNode node = lists.get(i);   
  12.         if(node!=null)  
  13.         {  
  14.             heap.offer(node);  
  15.         }  
  16.     }  
  17.     ListNode head = null;  
  18.     ListNode pre = head;  
  19.     while(heap.size()>0)  
  20.     {  
  21.         ListNode cur = heap.poll();  
  22.         if(head == null)  
  23.         {  
  24.             head = cur;  
  25.             pre = head;  
  26.         }  
  27.         else  
  28.         {  
  29.             pre.next = cur;  
  30.         }  
  31.         pre = cur;  
  32.         if(cur.next!=null)  
  33.             heap.offer(cur.next);  
  34.     }  
  35.     return head;  
  36. }  

可以看出两种方法有着同样的时间复杂度,都是可以接受的解法,但是却代表了两种不同的思路,数据结构也不用。个人觉得两种方法都掌握会比较好哈,因为在实际中比较有应用,所以也是比较常考的题目。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值