[LeetCode] Flatten a Multilevel Doubly Linked List 壓平一個多層的雙向連結串列
You are given a doubly linked list which in addition to the next and previous pointers, it could have a child pointer, which may or may not point to a separate doubly linked list. These child lists may have one or more children of their own, and so on, to produce a multilevel data structure, as shown in the example below.
Flatten the list so that all the nodes appear in a single-level, doubly linked list. You are given the head of the first level of the list.
Example:
Input: 1---2---3---4---5---6--NULL | 7---8---9---10--NULL | 11--12--NULL Output: 1-2-3-7-8-11-12-9-10-4-5-6-NULL
Explanation for the above example:
Given the following multilevel doubly linked list:
We should return the following flattened doubly linked list:
這道題給了我們一個多層的雙向連結串列,讓我們壓平成為一層的雙向連結串列,題目中給了形象的圖例,不難理解題意。根據題目中給的例子,我們可以看出如果某個結點有下一層雙向連結串列,那麼下一層雙向連結串列中的結點就要先加入進去,如果下一層連結串列中某個結點還有下一層,那麼還是優先加入下一層的結點,整個加入的機制是DFS的,就是有岔路先走岔路,走到沒路了後再返回,這就是深度優先遍歷的機制。好,那麼既然是DFS,肯定優先考慮遞迴啦。方法有了,再來看具體怎麼遞迴。由於給定的多層連結串列本身就是雙向的,所以我們只需要把下一層的結點移到第一層即可,那麼沒有子結點的結點就保持原狀,不作處理。只有對於那些有子結點的,我們需要做一些處理,由於子結點連結的雙向連結串列要加到後面,所以當前結點之後要斷開,再斷開之前,我們用變數next指向下一個連結串列,然後我們對子結點呼叫遞迴函式,我們suppose返回的結點已經壓平了,那麼就只有一層,那麼就相當於要把這一層的結點加到斷開的地方,所以我們需要知道這層的最後一個結點的位置,我們用一個變數last,來遍歷到壓平的這一層的末結點。現在我們就可以開始連結了,首先把子結點鏈到cur的next,然後把反向指標prev也鏈上。此時cur的子結點child可以清空,然後壓平的這一層的末節點last鏈上之前儲存的next結點,如果next非空,那麼鏈上反向結點prev。這些操作完成後,我們就已經將壓平的這一層完整的加入了之前層斷開的地方,我們繼續在之前層往下遍歷即可,參見程式碼如下:
解法一:
class Solution { public: Node* flatten(Node* head) { Node *cur = head; while (cur) { if (cur->child) { Node *next = cur->next; cur->child = flatten(cur->child); Node *last = cur->child; while (last->next) last = last->next; cur->next = cur->child; cur->next->prev = cur; cur->child = NULL; last->next = next; if (next) next->prev = last; } cur = cur->next; } return head; } };
我們其實也可以不用遞迴,連結串列的題不像樹的題,對於樹的題使用遞迴可以很簡潔,而連結串列遞迴和迭代可能差的並不多。如果你仔細對比兩種方法的程式碼,你會發現迭代的寫法剛好比遞迴的寫法少了呼叫遞迴的那一行,給人一種完全沒有必要使用遞迴的感覺,其實兩種解法的操作順序不同的,遞迴寫法是從最底層開始操作,先把最底層加入倒數第二層,再把混合後的層加入倒數第三層,依此類推,直到都融合到第一層為止。而迭代的寫法卻是反過來的,先把第二層加入第一層,此時第二層底下可能還有很多層,不必理會,之後等遍歷到的時候,再一層一層的加入第一層中,不管哪種方法,最終都可以壓平,參見程式碼如下:
解法二:
class Solution { public: Node* flatten(Node* head) { Node *cur = head; while (cur) { if (cur->child) { Node *next = cur->next; Node *last = cur->child; while (last->next) last = last->next; cur->next = cur->child; cur->next->prev = cur; cur->child = NULL; last->next = next; if (next) next->prev = last; } cur = cur->next; } return head; } };
類似題目:
參考資料: