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ū)所集錄》