runtime 16 ms, beats 99.38% of cpp submissionsO(nlog(k)) solution with explanation
tags : linked list, heap, divide and conquer
🔗 link Merge k Sorted Lists
📖 description 給定 k 個 sorted list ,將他們 merge 成一個 sorted list 。
🧠 solution 最簡單的想法是從頭到尾兩兩 merge 起來,但這樣做的話永遠有一邊較長的 (很多 list 合成的結果),所以必須想辦法每次 merge 的時候都是挑到最短的兩個 list ,這樣做的計算消耗是比較低的。
heap (by length) 於是便可以使用 priority queue 來達成,我們可以以 list 的長度作為比較的依據,並維護一個 min heap ,讓每次 merge 都從小的開始。
ex.
[
1. 2->6 (長度 2 )
2. 1->3->4 (長度 3 )
3. 1->4->5 (長度 3 )
]
先 merge 1、2
[
1. 1->4->5 (長度 3 )
2. 1->2->3->4->6 (長度 5 )
]
再 merge 1、2
[
1. 1->1->2->3->4->4->5->6 (長度 8 )
]
divide and conquer (此方法的時間消耗比較少,且空間複雜度也比較低) 為了達到每次都挑最短 list 的目的,需要在一開始時遍歷所有的 list 來獲得長度,但也可以單純不要一直用到 merge 完的結果,使用 merge sort 的概念,針對每一層向下 merge 出第二層,雖然沒辦法確保每次挑到的都是最好的,但可以用多遍歷一次所有的 list 。
list0 list1 list2 list3 list4 list5
\ / \ / \ /
list0 list2 list4
\ / |
list0 list4
\ /
list0
⏳ time complexity n 表示所有 list 總和的長度, k 表示有幾個 list ,在 priority queue 內共有 log(k) 次操作 總時間複雜度 O(nlog(k))
📝 code heap (by length) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 struct list_obj { ListNode* lis; int len; bool operator <(const list_obj& a) const { return len>a.len; } }; class Solution {public : int get_len (ListNode* head) { auto tmp = head; int cnt = 0 ; while (tmp!=nullptr ){ tmp = tmp->next; cnt++; } return cnt; } ListNode* merge (ListNode* a, ListNode* b) { auto head = new ListNode (); auto tmp = head; ListNode* node; while (a!=nullptr && b!=nullptr ){ if (a->val<b->val){ node = a; a = a->next; } else { node = b; b = b->next; } tmp->next = node; tmp = tmp->next; } if (a!=nullptr ){ tmp->next = a; } if (b!=nullptr ){ tmp->next = b; } return head->next; } ListNode* mergeKLists (vector<ListNode*>& lists) { if (lists.size ()==0 ){ return nullptr ; } priority_queue<list_obj> pq; for (int i=0 ;i<lists.size ();i++){ pq.push (list_obj{lists[i],get_len (lists[i])}); } while (pq.size ()>=2 ){ auto a = pq.top (); pq.pop (); auto b = pq.top (); pq.pop (); pq.push (list_obj{merge (a.lis,b.lis),a.len+b.len}); } return pq.top ().lis; } };
divide and conquer 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 class Solution {public : ListNode* merge (ListNode* a, ListNode* b) { if (a==nullptr ){ return b; } else if (b==nullptr ){ return a; } ListNode* head; if (a->val<=b->val){ head=a; a=a->next; } else { head=b; b=b->next; } auto tmp = head; ListNode* node; while (a!=nullptr && b!=nullptr ){ if (a->val<b->val){ node = a; a = a->next; } else { node = b; b = b->next; } tmp->next = node; tmp = tmp->next; } if (a!=nullptr ){ tmp->next = a; } if (b!=nullptr ){ tmp->next = b; } return head; } ListNode* merge_all (vector<ListNode*>& lists,int l,int r) { if (l>=r){ return lists[l]; } int mid = (r+l)/2 ; ListNode* left = merge_all (lists,l,mid); ListNode* right = merge_all (lists,mid+1 ,r); return merge (left,right); } ListNode* mergeKLists (vector<ListNode*>& lists) { if (lists.size ()==0 ){ return nullptr ; } return merge_all (lists,0 ,lists.size ()-1 ); } };