Merge k sorted linked lists and return it as one sorted list. Analyze and describe its complexit

题目:

Merge k sorted linked lists and return it as one sorted list. Analyze and describe its complexity.

这道题目在分布式系统中非常常见,来自不同client的sorted list要在central server上面merge起来。这个题目一般有两种做法,下面一一介绍并且分析复杂度。 第一种做法比较容易想到,就是有点类似于MergeSort的思路,就是分治法,不了解MergeSort的朋友,请参见 归并排序-维基百科,是一个比较经典的O(nlogn)的排序算法,还是比较重要的。思路是先分成两个子任务,然后递归求子任务,最后回溯回来。这个题目也是这样,先把k个list分成两半,然后继续划分,知道剩下两个list就合并起来,
合并时会用到 Merge Two Sorted Lists 这道题,不熟悉的朋友可以复习一下。代码如下: 
 
 1 public class Solution {
 2 
 3     public ListNode mergeKLists(ListNode[] lists) {
 4         if(lists.length == 0) {
 5             return null;
 6         }
 7 
 8         return merge(0, lists.length-1, lists);
 9     }
10 
11     public ListNode merge(int i, int j, ListNode[] lists) {
12         if(j < i) {
13             return null;
14         }
15 
16         if(i == j) {
17             return lists[i];
18         }
19 
20         int mid = (j-i)>>1 + i;
21         ListNode l = merge(i, mid, lists);
22         ListNode r = merge(mid+1, j, lists);
23 
24         ListNode dummy = new ListNode(0);
25         ListNode runner = dummy;
26 
27         while(l != null && r != null) {
28             if(l.val > r.val) {
29                 runner.next = r;
30                 r = r.next;
31                 runner = runner.next;
32             } else {
33                 runner.next = l;
34                 l = l.next;
35                 runner = runner.next;
36             }
37         }
38 
39         if(l == null && r == null) {
40             return dummy.next;
41         }
42 
43         if(l == null) {
44             runner.next = r;
45         } else {
46             runner.next = l;
47         }
48 
49         return dummy.next;
50     }
51 }

我们来分析一下上述算法的时间复杂度。假设总共有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)


 1 public class Solution {
 2 
 3     public ListNode mergeKLists(ArrayList<ListNode> lists) {
 4         PriorityQueue<ListNode> heap = new PriorityQueue<ListNode>(10, new Comparator<ListNode>(){
 5             public int compare(ListNode n1, ListNode n2) {
 6                 return n1.val - n2.val;
 7             }
 8         });
 9 
10         for(int i = 0; i < lists.size(); i++) {
11             ListNode node = lists.get(i);
12             if(node != null) {
13                 heap.offer(node);
14             }
15         }
16 
17         ListNode head = null;
18         ListNode pre = head;
19 
20         while(head.size() > 0) {
21             ListNode current = head.poll();
22             if(head == null) {
23                 head = null;
24                 pre = head;
25             } else {
26                 pre.next = current;
27             }
28 
29             pre = current;
30             if(current.next != null) {
31                 heap.offer(current.next);
32             }
33         }
34 
35         return head;
36     }
37 }
原文地址:https://www.cnblogs.com/wylwyl/p/10477891.html