LeetCode 105. 從前序與中序遍歷序列構(gòu)造二叉樹(shù) | Python

105. 從前序與中序遍歷序列構(gòu)造二叉樹(shù)


題目來(lái)源:https://leetcode-cn.com/problems/construct-binary-tree-from-preorder-and-inorder-traversal

題目


根據(jù)一棵樹(shù)的前序遍歷與中序遍歷構(gòu)造二叉樹(shù)。

注意:

你可以假設(shè)樹(shù)中沒(méi)有重復(fù)的元素。

例如,給出

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

返回如下的二叉樹(shù):

    3
   / \
  9  20
    /  \
   15   7

解題思路


思路:遞歸

在這里,先講一下前序遍歷和中序遍歷的概念。

  • 前序遍歷:首先訪問(wèn)根節(jié)點(diǎn),然后遍歷左子樹(shù),最后遍歷右子樹(shù)。
  • 中序遍歷:首先遍歷左子樹(shù),然后訪問(wèn)根節(jié)點(diǎn),最后遍歷右子樹(shù)。

即是說(shuō)兩者的遍歷順序分別為:

  • 前序遍歷:根節(jié)點(diǎn) -> 左子樹(shù) -> 右子樹(shù)
  • 中序遍歷:左子樹(shù) -> 根節(jié)點(diǎn) -> 右子樹(shù)

根據(jù)上面的順序可以看到,前序遍歷中,第一個(gè)就是根節(jié)點(diǎn);而中序遍歷中,根節(jié)點(diǎn)的左側(cè)是左子樹(shù),右側(cè)是右子樹(shù)。根據(jù)這個(gè)特性,先結(jié)合例子看下。

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

在這個(gè)例子當(dāng)中,前序遍歷 preorder 的第一個(gè)元素 3 即是根節(jié)點(diǎn),再看中序遍歷, inorder 中 3 的左邊 [9] 即是左子樹(shù),而右邊 [15, 20, 7] 即是右子樹(shù)。根據(jù)這個(gè)思路,就能構(gòu)造出完整的二叉樹(shù)。

這里說(shuō)下具體的思路:

  • 首先找到根節(jié)點(diǎn)(依據(jù):前序遍歷順序,先遍歷根節(jié)點(diǎn))
  • 構(gòu)建根節(jié)點(diǎn)的左子樹(shù)(依據(jù):中序遍歷,根節(jié)點(diǎn)的左側(cè)為左子樹(shù))
  • 構(gòu)建根節(jié)點(diǎn)的右子樹(shù)(依據(jù):中序遍歷,根節(jié)點(diǎn)的右側(cè)為右子樹(shù))

題目中,有個(gè)提示:【假設(shè)樹(shù)中沒(méi)有重復(fù)的元素】。依據(jù)這個(gè)提示,我們?cè)谇靶虮闅v中找到的根節(jié)點(diǎn)元素,可根據(jù)元素值在中序遍歷中定位它的位置。

具體的代碼實(shí)現(xiàn)如下。

代碼實(shí)現(xiàn)


# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution:
    def buildTree(self, preorder: List[int], inorder: List[int]) -> TreeNode:
        def build_tree(pre_left, pre_right, in_left, in_right):
            """構(gòu)建二叉樹(shù)

            Args:
                pre_left: 前序遍歷左邊界
                pre_right: 前序遍歷右邊界
                in_left: 后序遍歷左邊界
                in_right: 后序遍歷右邊界
            """
            # 終止條件
            if in_left > in_right:
                return None

            # 根據(jù)前序遍歷的順序,第一個(gè)元素就是根節(jié)點(diǎn)
            pre_root = pre_left
            # 構(gòu)建根節(jié)點(diǎn)
            root = TreeNode(preorder[pre_root])

            # 因?yàn)轭}目提示,假設(shè)樹(shù)中沒(méi)有重復(fù)元素
            # 那么根據(jù)根節(jié)點(diǎn)的值,在 inorder 中定位
            in_root = inorder.index(root.val)
            # 根據(jù)中序遍歷的訪問(wèn)順序,根節(jié)點(diǎn)的左邊即是左子樹(shù),右邊是右子樹(shù)
            # 在這里先得到左子樹(shù)節(jié)點(diǎn)數(shù)目
            size_left_subtree = in_root - in_left
            # 構(gòu)建左子樹(shù)
            # 在這里中序遍歷根節(jié)點(diǎn)左邊部分的節(jié)點(diǎn)(不包含根節(jié)點(diǎn)),其實(shí)就等同于前序遍歷左邊界下一位 + size_left_subtree 個(gè)節(jié)點(diǎn)
            # 即是 in_left 到 inroot 前一位這部分節(jié)點(diǎn),等同于 pre_left 的下一位開(kāi)始的 size_left_subtree 個(gè)元素
            root.left = build_tree(pre_left+1, pre_left+size_left_subtree, in_left, in_root-1)
            # 構(gòu)建右子樹(shù)
            # 此時(shí)中序遍歷根節(jié)點(diǎn)右邊部分的節(jié)點(diǎn)(不包含根節(jié)點(diǎn)),對(duì)應(yīng)前序遍歷左邊界 + 1 + sub_left_subtree 開(kāi)始到其右邊界
            root.right = build_tree(pre_left+1+size_left_subtree, pre_right, in_root + 1, in_right)

            return root

        size = len(inorder)

        return build_tree(0, size-1, 0, size-1)

實(shí)現(xiàn)結(jié)果


實(shí)現(xiàn)結(jié)果

總結(jié)


  • 首先在根據(jù)前序遍歷訪問(wèn)的順序,先找到二叉樹(shù)的根節(jié)點(diǎn),構(gòu)建根節(jié)點(diǎn)
  • 因?yàn)轭}目說(shuō)明可假設(shè)無(wú)重復(fù)元素,那么可依據(jù)上面找到根節(jié)點(diǎn)的值,在中序遍歷 inorder 中找到其位置。
  • 依據(jù)中序遍歷的訪問(wèn)順序,可確定當(dāng)前找到的根節(jié)點(diǎn)左側(cè)是左子樹(shù),右側(cè)部分是右子樹(shù)
  • 那么根據(jù)上面分析的情況,遞歸構(gòu)建左子樹(shù),右子樹(shù)。

歡迎關(guān)注微信公眾號(hào)《書(shū)所集錄》

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。