Merge k sorted linked lists and return it as one sorted list.
題意:把k個已經排好序的鏈表整合到一個鏈表中,並且這個鏈表是排了序的。
題解:這是一道經典好題,值得仔細一說。
有兩種方法,假設每個鏈表的平均長度是n,那么這兩種方法的時間復雜度都是O(nklogk)。
方法一:
基本思路是:把k個鏈表開頭的值排個序,每次取最小的一個值放到答案鏈表中,這次取完之后更新這個值為它后面的一個值。接着這么取一直到全部取完。那么每次更新之后怎么對當前這k個值重新排序以便知道當前最小的是誰呢?用優先隊列(或者堆)來維護這k個值就好啦!
由於每個值都要取一次,一共取nk次。每次更新優先隊列要logk的復雜度。所以總時間復雜度為O(nklogk);空間復雜度為優先隊列所占空間,為O(k)。
/** * Definition for singly-linked list. * struct ListNode { * int val; * ListNode *next; * ListNode(int x) : val(x), next(NULL) {} * }; */ class Solution { public: struct cmp { bool operator() (const ListNode* a,const ListNode* b) { return a->val > b->val; } }; ListNode* mergeKLists(vector<ListNode*>& lists) { priority_queue<ListNode*, vector<ListNode*>, cmp>pq; for(auto i:lists) { if(i) //這句判斷很有必要,不能把空的加入隊列。比如這組數據:[[],[]] { pq.push(i); } } if(pq.empty()) { return nullptr; } ListNode* ans = pq.top(); pq.pop(); ListNode* tail = ans; if(tail->next) { pq.push(tail->next); } while(!pq.empty()) { tail->next = pq.top(); tail = tail->next; pq.pop(); if(tail->next) { pq.push(tail->next); } } return ans; } };
方法二:
和方法一的思路不同:考慮分治的思想來解這個題(類似歸並排序的思路)。把這些鏈表分成兩半,如果每一半都合並好了,那么我就最后把這兩個合並了就行了。這就是分治法的核心思想。
但是這道題由於存的都是指針,就具有了更大的操作靈活性,可以不用遞歸來實現分治。就是先兩兩合並后在兩兩合並。。。一直下去直到最后成了一個。(相當於分治算法的那棵二叉樹從底向上走了)。
第一次兩兩合並是進行了k/2次,每次處理2n個值。
第二次兩兩合並是進行了k/4次,每次處理4n個值。
。。。
最后一次兩兩合並是進行了k/(2^logk)次,每次處理2^logK*N個值。
所以時間復雜度:
O((2N) * (K / 2) + (4N) * (K / 4) + (8N) * (K / 8) + .............. + (2^logK*N) * (K / (2 ^logK)) )=O( logK*KN)
空間復雜度是O(1)。
/** * Definition for singly-linked list. * struct ListNode { * int val; * ListNode *next; * ListNode(int x) : val(x), next(NULL) {} * }; */ class Solution { public: ListNode *mergeKLists(vector<ListNode *> &lists) { if(lists.empty()){ return nullptr; } while(lists.size() > 1){ lists.push_back(mergeTwoLists(lists[0], lists[1])); lists.erase(lists.begin()); lists.erase(lists.begin()); } return lists.front(); } ListNode *mergeTwoLists(ListNode *l1, ListNode *l2) { if(l1 == nullptr){ return l2; } if(l2 == nullptr){ return l1; } if(l1->val <= l2->val){ l1->next = mergeTwoLists(l1->next, l2); return l1; } else{ l2->next = mergeTwoLists(l1, l2->next); return l2; } } };