1. 程式人生 > 實用技巧 >LeetCode Notes_#430_扁平化多級雙向連結串列

LeetCode Notes_#430_扁平化多級雙向連結串列

LeetCode Notes_#430_扁平化多級雙向連結串列

LeetCode

Contents

題目

多級雙向連結串列中,除了指向下一個節點和前一個節點指標之外,它還有一個子連結串列指標,可能指向單獨的雙向連結串列。這些子列表也可能會有一個或多個自己的子項,依此類推,生成多級資料結構,如下面的示例所示。
給你位於列表第一級的頭節點,請你扁平化列表,使所有結點出現在單級雙鏈表中。

示例 1:

輸入:head = [1,2,3,4,5,6,null,null,null,7,8,9,10,null,null,11,12]
輸出:[1,2,3,7,8,11,12,9,10,4,5,6]
解釋:

輸入的多級列表如下圖所示:

扁平化後的連結串列如下圖:

提示:

  • 節點數目不超過 1000
  • 1 <= Node.val <= 10^5

思路

我們將列表順時針轉 90 °,那麼就會看到一顆二叉樹,則扁平化的操作也就是對二叉樹進行先序遍歷(深度優先搜尋)。更準確的說,除了按照先序遍歷的順序訪問,還需要注意的還需要按對節點進行雙向連線。

在圖中,我們將child看作二叉樹的left指標,將next指標看作二叉樹中的right指標。

遞迴函式Node flattenDFS(Node prev, Node cur)

兩個引數就是扁平化後的雙向連結串列的相鄰兩個節點。
返回值是tail

節點,即每一個節點的最小的左孩子節點。

終止條件

cur == null,說明它的前一個節點prev就已經是最左下角的那個節點(即tail),直接返回prev

遞推過程

  1. prevnext之間建立雙向連線
  2. 暫存cur.next節點到變數tmpNext中,因為cur.next在遞迴呼叫過程中會發生改變
  3. 遞迴呼叫Node tail = flattenDFS(cur, cur.child);,將連結串列看成是二叉樹的話,相當於遞迴訪問左子樹的過程。
    • 將每對具有父子關係的節點建立雙向連線。
    • 返回tail節點,即當前已經做完扁平化處理的連結串列的最後一個節點,這個節點要連線到tmpNext
  4. 刪除cur.child指標,原因是cur/cur_child之間已經建立了雙向連線,child指標就不需要了。
  5. 遞迴呼叫並返回值return flattenDFS(tail, tmpNext);,將連結串列看成是二叉樹的話,相當於遞迴訪問右子樹的過程。
    • 左子樹的最後一個節點是剛才記下的tail,將其連線到右子樹的第一個節點tmpNext
    • 返回值是tmpNext的最左下角節點。

解答

class Solution {
    public Node flatten(Node head) {
        if(head == null) return head;
        //因為head沒有prev指標,所以新增一個dummyHead,dummyHead.next = head,那麼可以直接呼叫遞迴函式處理
        Node dummyHead = new Node(0, null, head, null);
        flattenDFS(dummyHead, head);
        //斷開head.prev指標
        dummyHead.next.prev = null;
        return dummyHead.next;
    }
    //這裡的prev/cur指的是在扁平列表中,需要建立雙向連線的兩個相鄰節點,可能是父子關係,也可能本來就是相鄰關係
    //分為cur有孩子,沒孩子兩種情況
    private Node flattenDFS(Node prev, Node cur){
        if(cur == null) return prev;
        cur.prev = prev;
        prev.next = cur;
        //cur.next可能會改變,所以要提前儲存
        Node tmpNext = cur.next;
        //將child看作左子樹,在cur和cur.child之間建立雙向連線
        //tail的意思是,如果cur.child還有左孩子,那麼會不斷遞迴,直到最小的左孩子,記作tail
        //有孩子的情況下,tail是最小的孩子,沒孩子的情況下,tail就是cur本身
        Node tail = flattenDFS(cur, cur.child);
        //已經建立了雙向連線後,就不需要child指標了,將它斷開
        cur.child = null;
        //有孩子的情況下,將最小的左孩子遍歷完之後,之後連線第一個右孩子
        return flattenDFS(tail, tmpNext);
    }
}

複雜度分析

時間複雜度:O(n),n是節點個數。
空間複雜度:O(n),遞迴呼叫層數是n,則佔用棧空間是O(n)。