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)