【二叉樹】106. 從中序與後序遍歷序列構造二叉樹 【中等】

給定兩個整數數組 inorder 和 postorder ,其中 inorder 是二叉樹的中序遍歷, postorder 是同一棵樹的後序遍歷,請你構造並返回這棵 二叉樹 。

示例1:

 

輸入:inorder = [9,3,15,20,7], postorder = [9,15,7,20,3]
輸出:[3,9,20,null,null,15,7]
示例 2:

輸入:inorder = [-1], postorder = [-1]
輸出:[-1]
 

提示:

1 <= inorder.length <= 3000
postorder.length == inorder.length
-3000 <= inorder[i], postorder[i] <= 3000
inorder 和 postorder 都由 不同 的值組成
postorder 中每一個值都在 inorder 中
inorder 保證是樹的中序遍歷
postorder 保證是樹的後序遍歷

 

分析

一句話題解:

二叉樹後續遍歷的最後一個元素一定是根節點;

二叉樹的根節點把中序遍歷序列分成兩個區間:

  左邊區間的所有節點構成根節點的左子樹;

  右邊區間的所有節點構成根節點的右子樹。

遞歸構建二叉樹即可。

思路分析:

解決二叉樹的問題通常會用到分治思想,分治思想一般通過遞歸方法實現。

複習一下分治法的思想:把原問題分解成若干個於原問題結構相同當規模更小的子問題,待子問題解決以後,再合併它們,原問題就得以解決。

歸併排序和快速排序是典型的分治思想的應用,其中:

  歸併排序先無腦“分”,在“合”的時候就嘛煩一些;

  快速排序在“分”上花了很多時間,然後就遞歸處理下去就好了,沒有在“合”的過程。

以題目中給出的例子爲例,講解如何構建二叉樹。

中序遍歷inorder = [9,3,15,20,7]

後序遍歷postorder = [9,15,7,20,3]

 

 注意:這道題並不難,在草稿紙上寫寫畫畫,就能把思路想清楚,但是在編碼上會有一些小陷阱,在計算索引邊界值的時候要仔細一些。

下面給我兩種寫法,區別在於空間複雜度:

方法一:在遞歸方法中,傳入數組的拷貝(不推薦,複雜度高)

該方法在計算索引的時候會稍微容易一些。

from typing import List


class Solution:
    def buildTree(self, inorder: List[int], postorder: List[int]) -> TreeNode:
        assert len(inorder) == len(postorder)

        if len(inorder) == 0:
            return None
        if len(inorder) == 1:
            # 這裏要返回結點,而不是返回具體的數
            return TreeNode(inorder[0])
        # 後序遍歷的最後一個結點就是根結點
        root = TreeNode(postorder[-1])
        # 在中序遍歷中找到根結點的索引,得到左右子樹的一個劃分
        pos = inorder.index(postorder[-1])
        # 這裏的列表切片使用的是複製值,使用了一些空間,因此空間複雜度是 O(N)
        root.left = self.buildTree(inorder[:pos], postorder[:pos])
        root.right = self.buildTree(inorder[pos + 1:], postorder[pos:-1])
        return root

複雜度分析:時間複雜度:O(N2), 這裏 N 是二叉樹的結點個數,算法中每個結點都會被看到一次,而查找根結點在中序遍歷序列中的位置,又需要遍歷一次,遞歸的深度是對數級別的,因此時間複雜度是O(N2)。

空間複雜度:O(N),構造一棵樹需要 N 個結點。

 

方法二:在遞歸方法中,傳入子數組的邊界下標

注意:在遞歸方法中,有一個數組的邊界下標。

計算的依據是遞歸方法傳入的中序遍歷數組(的子數組)和後序遍歷遍歷數組(的子數組)的長度相等。這裏採用的辦法是解方程計算未知數,具體需要計算哪個參數在下面代碼中已經註明了。

下面展示了一個計算邊界的方法:

 

這樣,可以在遞歸構造前,把中序遍歷的值和下標放在哈希表中,就不需要通過遍歷得到當前根結點在中序遍歷中的位置了。

from typing import List


class Solution:

    def __init__(self):
        self.inorder = None
        self.postorder = None

    def buildTree(self, inorder: List[int], postorder: List[int]) -> TreeNode:
        assert len(inorder) == len(postorder)
        size = len(inorder)

        self.inorder = inorder
        self.postorder = postorder
        return self.__dfs(0, size - 1, 0, size - 1)

    def __dfs(self, in_l, in_r, post_l, post_r):
        if in_l > in_r or post_l > post_r:
            return None

        val = self.postorder[post_r]
        # 後序遍歷的最後一個結點就是根結點
        root = TreeNode(val)
        # 在中序遍歷中找到根結點的索引,得到左右子樹的一個劃分
        pos = self.inorder.index(val)

        # 注意:第 4 個參數是計算出來的,依據:兩邊區間長度相等
        root.left = self.__dfs(in_l, pos - 1, post_l, pos - 1 - in_l + post_l)
        # 注意:第 3 個參數是計算出來的,依據:兩邊區間長度相等
        root.right = self.__dfs(pos + 1, in_r, post_r - in_r + pos, post_r - 1)
        return root

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章