Title
根据一棵树的前序遍历与中序遍历构造二叉树。
注意:
你可以假设树中没有重复的元素。
例如,给出
前序遍历 preorder = [3,9,20,15,7]
中序遍历 inorder = [9,3,15,20,7]
返回如下的二叉树:
3
/ \
9 20
/ \
15 7
Solve
前言
二叉树前序遍历的顺序为:根节点→递归遍历左子树→递归遍历右子树。
二叉树中序遍历的顺序为:递归遍历左子树→根节点→递归遍历右子树。
Recursion
对于任意一颗树而言,前序遍历的形式为:
[ 根节点, [左子树的前序遍历结果], [右子树的前序遍历结果] ]
即根节点总是前序遍历中的第一个节点。
而中序遍历的形式为:
[ [左子树的中序遍历结果], 根节点, [右子树的中序遍历结果] ]
只要我们在中序遍历中定位到根节点,那么我们就可以分别知道左子树和右子树中的节点数目。由于同一颗子树的前序遍历和中序遍历的长度显然是相同的,因此我们可以对应到前序遍历的结果中,对上述形式中的左右括号就行定位。
这样我们就知道了左子树的前序遍历和中序遍历结果,以及右子树的前序遍历和中序遍历结果,可以递归地构造出左子树和右子树,再将这两颗子树连接到根节点的左右位置。
在中序遍历中对根节点进行定位时,可以考虑使用哈希映射(HashMap)来快速定位根节点。对于哈希映射中的每个键值对,键表示一个元素,值表示其在中序遍历中的出现位置。
在构造二叉树的过程之前,可以对中序遍历的列表进行一遍扫描,构造出这个哈希映射,此后二叉树的构造中只需要O(1)的时间对根节点进行定位。
复杂度分析
时间复杂度: O(n),其中 n 是树中的节点个数。
空间复杂度: O(n),除去返回的答案需要的 O(n) 空间之外,我们还需要使用 O(n) 的空间存储哈希映射,以及 O(h)(其中 h 是树的高度)的空间表示递归时栈空间。这里 h < n,所以总空间复杂度为 O(n)。
Code
def buildTree(self, preorder: List[int], inorder: List[int]) -> TreeNode:
def buildTree(preOrderLeft: int, preOrderRight: int, inOrderLeft: int, inOrderRight: int):
if preOrderLeft > preOrderRight:
return None
preRootIndex = preOrderLeft
inRootIndex = index[preorder[preRootIndex]]
root = TreeNode(preorder[preRootIndex])
leftSubTreeLength = inRootIndex - inOrderLeft
root.left = buildTree(preOrderLeft + 1, preOrderLeft + leftSubTreeLength, inOrderLeft, inRootIndex - 1)
root.right = buildTree(preOrderLeft + leftSubTreeLength + 1, preOrderRight, inRootIndex + 1, inOrderRight)
return root
length = len(preorder)
index = {value: key for key, value in enumerate(inorder)}
return buildTree(0, length - 1, 0, length - 1)