演算法基礎~連結串列~排序連結串列的合併(k條)

一樂樂 發表於 2021-07-21
演算法

演算法基礎~連結串列~排序連結串列的合併(k條)

1,題意:已知k個已排序連結串列頭結點指標,將這k個連結串列合併,合併後仍然為有序的,返回合併後的頭結點。

2,方法之間時間複雜度的比較:

方法1(藉助工具vector封裝好的sort方法):將k * n個結點放到vector,則原 vector的排序時間複雜度是 O(nlogn);

有k*n個結點的排序,時間複雜度是 O(knlog(kn));

方法2(分制後相連法),分制:這裡我們要想到高中的DNA複製~一個DNA複製生成K個DNA的過程。

【1----複製----》k個數】,複製了n次,結果有k個數,則2n = k

這裡我們反過來,想到是在融合,則【k個數----複製----》1】,這個過程需要融合的次數,跟當初 複製次數一樣,由2n = k,解得,n=logk。

●具體過程分析:

第一次,連結串列兩兩之間合併,進行 合併次數為 k/2,每次處理結點數字為2n個;

第二次,連結串列兩兩之間合併,進行 合併次數為 k/4,每次處理結點數字為4n個;

。。。

由上面推導的融合過程,知道最後一次,即 第 logk 次,連結串列兩兩之間進行 合併,進行合併次數為

k/2 logk,每次處理結點數字為2 logk n個;

時間複雜度:2n * k/2 + 4n * k/4 + 8n * k/8 + … + 2 logk n * k/2 logk = nk + nk + nk +… +nk = O(nklogk);

所以方法2,更優;

 

3,從方法2的分析過程,我們深深的感受到一種:把規模大的問題變成規模較小的;規模較小的問題又變成規模更小的問題,

小到一定程度可以直接得出它的解,從而得到問題的解。~沒錯,是遞迴的味道!

 

4,直接上程式碼,分析如上【程式碼中的mergeTwoLists(連結串列1頭指標,連結串列2頭指標)參考我們上一篇文章:

演算法基礎~連結串列~排序連結串列的合併(2條)》~ https://www.cnblogs.com/shan333/p/15041561.html】:

public class Solution {
  public:
    ListNode* mergeKLists(std::vector<ListNode*>& lists){
       if(lists.size() == 0){
           return NULL;
       }
       if(lists.size() == 1){
           return lists[0];
       }
       if(lists.size() == 2){
           return mergeTwoLists(lists[0],lists[1]);
       }
       int mid = lists.size() / 2;
       //拆分成兩個子lists
       std::vector<ListNode*> sub1_lists;
       std::vector<ListNode*> sub2_lists;
       for(int i = 0; i < mid; i++){
           sub1_lists.push_back(lists[i]);
       }
       for(int i = mid; i < lists.size(); i++){
           sub2_lists.push_back(lists[i]);
       }
       //遞迴,不斷的兩兩連結串列進行融合
       ListNode *l1 = mergeKList(sub1_lists);
       ListNode *l2 = mergeKList(sub2_lists);
       return mergeTwoLists(l1, l2);
    }
}

 

5,遞迴思想的使用:

遞迴演算法的思想是:把規模大的問題變成規模較小的;規模較小的問題又變成規模更小的問題,小到一定程度可以直接得出它的解,從而得到問題的解。

解決問題時,把一個問題轉化為一個新的問題,而這個新的問題的解決方法仍與原問題的解法相同,

只是所處理的物件有所不同,這些被處理的物件之間是有規律的遞增或遞減;

 

 

 

參考文章:《什麼情況下用遞迴?~https://blog.csdn.net/ggxxkkll/article/details/7524056