Merge k sorted linked lists and return it as one sorted list. Analyze and describe its complexity.
Hide Similar Problems
题目意思就是合并多个有序链表!采用分治递归!
第一种思路分析:分而治之,不断划分,直到变成两个有序链表合并的子问题!归并的动态图如右图所示:
本题建立在合并两个有序链表算法的基础之上,不断合并两个有序子链表,直到整个子链表合而为一。这道题目在分布式系统中非常常见,来自不同client的sorted list要在central server上面merge起来。
我们来分析一下上述算法的时间复杂度。假设总共有k个list,每个list的最大长度是n,那么运行时间满足递推式T(k) = 2T(k/2)+O(n*k)。根据主定理,可以算出算法的总复杂度是O(nklogk)。如果不了解主定理的朋友,可以参见 主定理-维基百科 。空间复杂度的话是递归栈的大小O(logk)。
代码实现如下:
1 #include<iostream> 2 #include<vector> 3 using namespace std; 4 5 struct ListNode { 6 int val; 7 ListNode *next; 8 ListNode(int x) : val(x), next(NULL) {} 9 }; 10 11 class Solution { 12 public: 13 ListNode* mergeKLists(vector<ListNode*>& lists) 14 { 15 if (lists.size() == 0) 16 return NULL; 17 if (lists.size() == 1) 18 return lists[0]; 19 //vector<ListNode*> res; 20 while (lists.size()!=1) 21 { 22 ListNode*temp= mergeTwoLists(lists[0], lists[1]); 23 lists.push_back(temp); 24 lists.erase(lists.begin()); 25 lists.erase(lists.begin()); 26 } 27 return lists[0]; 28 29 } 30 ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) 31 { 32 if (!l1) 33 return l2; 34 if (!l2) 35 return l1; 36 ListNode n1(-1); 37 n1.next = l1; 38 ListNode *ptr1 = l1, *pre1, *ptr2 = l2, *b2; 39 pre1 = &n1; 40 while (ptr2) 41 { 42 b2 = ptr2->next; 43 if (ptr1->val < ptr2->val) 44 { 45 ptr1 = ptr1->next; 46 pre1 = pre1->next;/// 47 if (ptr1 == NULL)//这儿必须让其终止!否则运行会出错的 48 break; 49 } 50 else 51 { 52 pre1->next = ptr2; 53 ptr2->next = ptr1; 54 ptr2 = b2; 55 pre1 = pre1->next;/// 56 } 57 } 58 if (ptr2 != NULL) 59 pre1->next = ptr2; 60 return n1.next; 61 } 62 }; 63 64 int main() 65 { 66 Solution test; 67 68 vector<ListNode*> val; 69 ListNode *n1 = NULL; 70 ListNode n2(-1),n3(5),n4(11); 71 n2.next = &n3; 72 n3.next = &n4; 73 ListNode *n5 = NULL; 74 ListNode n6(6), n7(10); 75 n6.next = &n7; 76 val.push_back(n1); 77 val.push_back(&n2); 78 val.push_back(n5); 79 val.push_back(&n6); 80 //cout << "the size of val:" << val.size() << " " << val.capacity() << endl; 81 ListNode* result = test.mergeKLists(val); 82 while (result) 83 { 84 cout << result->val << " "; 85 result = result->next; 86 } 87 return 0; 88 }
第二种思路分析(用到了堆这种数据结构):思路比较难想到,但是其实原理比较简单。维护一个大小为k的堆,每次取堆顶的最小元素放到结果中,然后读取该元素的下一个元素放入堆中,重新维护好。因为每个链表是有序的,每次又是去当前k个元素中最小的,所以当所有链表都读完时结束,这个时候所有元素按从小到大放在结果链表中。这个算法每个元素要读取一次,即是k*n次,然后每次读取元素要把新元素插入堆中要logk的复杂度,所以总时间复杂度是O(nklogk)。空间复杂度是堆的大小,即为O(k)。