LeetCode23 Merge k Sorted Lists
题意:
Merge k sorted linked lists and return it as one sorted list. Analyze and describe its complexity. (Hard)
分析:
方法1:
利用做过的merge 2 sorted list,将头两个归并,结果再与下一个归并,以此类推,归并完所有。
时间复杂度分析是个小问题, merge 2 sorted list的复杂度是O(n),本以为结果就是O(n * k)。
但是仔细考虑,随着归并不断进行,其中一个链表的长度不断增加,直至O(n * k),所以复杂度应该是O(n * k^2)
代码:
1 /** 2 * Definition for singly-linked list. 3 * struct ListNode { 4 * int val; 5 * ListNode *next; 6 * ListNode(int x) : val(x), next(NULL) {} 7 * }; 8 */ 9 class Solution { 10 private: 11 ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) { 12 ListNode dummy(0); 13 ListNode* head = &dummy; 14 while (l1 != nullptr && l2 != nullptr) { 15 if (l1 -> val < l2 -> val) { 16 head -> next = l1; 17 l1 = l1 -> next; 18 } 19 else { 20 head -> next = l2; 21 l2 = l2 -> next; 22 } 23 head = head -> next; 24 } 25 if (l1 != nullptr) { 26 head -> next = l1; 27 } 28 if (l2 != nullptr) { 29 head -> next = l2; 30 } 31 return dummy.next; 32 } 33 public: 34 ListNode* mergeKLists(vector<ListNode*>& lists) { 35 if (lists.size() == 0) { 36 return nullptr; 37 } 38 ListNode dummy(0); 39 ListNode* head = &dummy; 40 head -> next = lists[0]; 41 for (int i = 1; i < lists.size(); ++i) { 42 head -> next = mergeTwoLists(head->next, lists[i]); 43 } 44 return dummy.next; 45 46 } 47 };
方法2:
利用最小堆。将所有链表的第一个节点加入堆,然后每次取出值最小的节点,并将该节点的next节点加入堆中。堆为空后所以节点处理完,归并后链表。
堆的大小为k,所以插入删除取节点复杂度均为O(logk),共对O(nk)个节点进行操作,所以时间复杂度为O(nklogk)。
注:
本题除了算法思想本身,还有对于实现过程还是有需要总结的。
1) STL的priority_queue默认的是最大堆,需要重载比较函数。
首先一般来讲priority有三个参数,即
template < class T, class Container = vector<T>, class Compare = less<typename Container::value_type> > class priority_queue;
可以看出,2、3有缺省值,即表示用vector实现priority_queue,实现的是最大堆。所以可以不写这两个参数。
但是如果要修改比较方式,即第三个参数的话,第二个参数也要给出(以前没用过,真是查文档才知道)。
对于内置类型,如int,可以使用stl的functor如 greater<int>直接修改堆的大小属性,但是自定义节点,如本例的ListNode,需要自己重载。
2)对于传入比较函数,一种方法是重载类的operator <,但是leetcode中ListNode的定义是不能修改的。
还有就是写一个cmp函数,值得注意的是。funtor作为一个类需要重载其operator(),即对于本题,写为
struct cmp{ bool operator() (ListNode* l1, ListNode* l2) { return l1 -> val > l2 -> val; } };
另外,priority_queue的第三个参数不能只传函数,必须封装成个functor(类),开始写的时候传函数报错了。
总之这题收货还是不少,不论从算法角度还是语言角度。
代码:
1 /** 2 * Definition for singly-linked list. 3 * struct ListNode { 4 * int val; 5 * ListNode *next; 6 * ListNode(int x) : val(x), next(NULL) {} 7 * }; 8 */ 9 class Solution { 10 private: 11 struct cmp{ 12 bool operator() (ListNode* l1, ListNode* l2) { //why?! 13 return l1 -> val > l2 -> val; 14 } 15 }; 16 public: 17 ListNode* mergeKLists(vector<ListNode*>& lists) { 18 ListNode dummy(0); 19 ListNode* head = &dummy; 20 priority_queue<ListNode*, vector<ListNode*>, cmp> que; 21 for (int i = 0; i < lists.size(); ++i) { 22 if (lists[i] != nullptr) { 23 que.push(lists[i]); 24 } 25 } 26 while (!que.empty()) { 27 ListNode* temp = que.top(); 28 head -> next = temp; 29 head = head -> next; 30 que.pop(); 31 if (temp -> next != nullptr) { 32 que.push(temp->next); 33 } 34 } 35 return dummy.next; 36 } 37 };