1. 程式人生 > >演算法練習week7--leetcode23

演算法練習week7--leetcode23

題目大意:
給出若干已經排好序連結串列的頭節點指標,將它們合併成一個成序的連結串列,返回其頭結點指標。

示例:
給定連結串列:1->2->3 和 4->6->8,則應當返回連結串列 1->2->3->4->6->8的頭節點指標。

解題思路:
  一開始看到這道題,我覺得會比較複雜。因為要排成有序連結串列需要先逐個比較,再將節點插入對應的位置。後來聯想到之前使用過的雜湊表,變換思路如下:遍歷每一個連結串列,將每個節點的值插入到容器set中,同時建立一個雜湊表,儲存節點的值和它出現的次數。所有連結串列遍歷結束之後,所有出現過的節點值都儲存到了set中,並且按順序排好。此時,只需要再遍歷一遍set,再找到雜湊表中對應出現的次數,就可完成合並連結串列的建立。實現程式碼如下: 

     
  以上程式碼在本地通過了測試,但上傳到leetcode時,卻出現了編譯錯誤。似乎是因為它不允許在類外建立全域性變數。回過頭看題目,我發現有一個重要條件完全沒有派上用場:給出的連結串列是已經排好序的,這說明演算法複雜度肯定不是最優的。並且,實現程式碼中,我自行建立了集合、對映表,又新建了節點,導致演算法的空間複雜度也會很大,進一步降低了其實用性。 
  在discuss中,高票的C++解答是基於之前的兩連結串列合併問題的拓展。AC程式碼如下:

/**
 1. Definition for singly-linked list.
 2. struct ListNode {
 3.     int val;
 4.     ListNode *next;
 5.     ListNode(int x) : val(x), next(NULL) {}
 6. };
 */
class Solution {
public:
    ListNode* mergeKLists(vector<ListNode*>& lists) {
    if(lists.size()==0)
        return nullptr;
    while(lists.size()>1)
    {
        lists.push_back(merge2Lists(lists[0],lists[1]));
        lists.erase(lists.begin());
        lists.erase(lists.begin());
    }
    return lists.front();      
    }
    ListNode* merge2Lists(ListNode* l1, ListNode* l2){  //連線兩個連結串列
    if(l1==nullptr)
        return l2;  //如果是空指標,則返回l1
    if(l2==nullptr)
        return l1;
    if(l1->val>l2->val)  
    {   
        l2->next=merge2Lists(l1,l2->next);  //確定下下一節點
        return l2;  //返回比較結果
    }
    else
    {
        l1->next=merge2Lists(l1->next,l2);
        return l1;
    }
    }
};

 

可以看出,它在時間和空間複雜度上都更加優越。經仔細分析,在合併兩個連結串列時,它是通過遞迴的方式尋找當前節點的下一節點,直到有一條連結串列為空為止。 
  對我來說,遞迴一直是一個理解上的難點。因此我試著從幾個例子中去理解它的意思。以連結串列1:1->2->3和鏈:2:2->3->4為例: 
  1. 首先需要尋找合成連結串列的頭結點,換言之,兩個連結串列頭結點中值較小的那一個,即1。 
  2. 隨後尋找下一節點。此時,需要比較當前節點在本連結串列中的下一節點與另一連結串列的當前節點值的大小,較小者為下一節點。此處,為2和2,不妨取連結串列2中的“2”作為下一節點。 
  3. 再次尋找下一節點,與步驟2類似,比較2與3,連結串列1中的“2”成為下一節點 
…… 
  4. 重複以上過程,當需要確定連結串列1中的3的下一節點時,發現它在本連結串列中已經是最後一個節點。因此另一連結串列中的節點自動成為下一節點。由於兩個連結串列本身是成序的,因此也無需進行下一步的搜尋,即可得到結果。 
  如此,只需要用一個函式merge2Lists即可實現上述過程。不難發現,如果採用順序的思維,是很容易理解以上的程式碼的。但輪到自己去實現時,仍然很難完成。歸根結底,是因為習慣了先程式中得到結果,再將結果進行進一步的處理的方式,不習慣遞迴的思維方法——先進行處理,再將結果逐步迴帶。