根据一棵树的前序遍历与中序遍历构造二叉树。
注意:
你可以假设树中没有重复的元素。
例如,给出
1 2
| 前序遍历 preorder = [3,9,20,15,7] 中序遍历 inorder = [9,3,15,20,7]
|
返回如下的二叉树:
递归
二叉树前序遍历的顺序为:
先遍历根节点;
随后递归地遍历左子树;
最后递归地遍历右子树。
二叉树中序遍历的顺序为:
先递归地遍历左子树;
随后遍历根节点;
最后递归地遍历右子树。
在「递归」地遍历某个子树的过程中,我们也是将这颗子树看成一颗全新的树,按照上述的顺序进行遍历。挖掘「前序遍历」和「中序遍历」的性质,我们就可以得出本题的做法。
对于任意一颗树而言,前序遍历的形式总是
1
| [ 根节点, [左子树的前序遍历结果], [右子树的前序遍历结果] ]
|
即根节点总是前序遍历中的第一个节点。而中序遍历的形式总是
1
| [ [左子树的中序遍历结果], 根节点, [右子树的中序遍历结果] ]
|
只要我们在中序遍历中定位到根节点,那么我们就可以分别知道左子树和右子树中的节点数目。
由于同一颗子树的前序遍历和中序遍历的长度显然是相同的,因此我们就可以对应到前序遍历的结果中,对上述形式中的所有左右括号进行定位。
这样以来,我们就知道了左子树的前序遍历和中序遍历结果,以及右子树的前序遍历和中序遍历结果,
我们就可以递归地对构造出左子树和右子树,再将这两颗子树接到根节点的左右位置。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| class Solution: def buildTree(self, preorder: List[int], inorder: List[int]) -> TreeNode: def myBuildTree(pre_left,pre_right, in_left,in_right): if pre_left > pre_right: return None
preorder_root = pre_left inorder_root = index[preorder[preorder_root]]
root = TreeNode(preorder[preorder_root])
size_left_subtree = inorder_root - in_left
root.left = myBuildTree(pre_left+1,pre_left+size_left_subtree,in_left,inorder_root-1)
root.right = myBuildTree(pre_left + size_left_subtree + 1, pre_right, inorder_root + 1, in_right) return root n = len(preorder) index = {element: i for i, element in enumerate(inorder)} return myBuildTree(0, n - 1, 0, n - 1)
|
迭代
对于前序遍历中的任意两个连续节点 u和v,根据前序遍历的流程,我们可以知道u ,v只有两种可能的关系:
- v 是 u的左儿子。这是因为在遍历到 u之后,下一个遍历的节点就是 u 的左儿子,即 v;
- u 没有左儿子,并且 v 是 u 的某个祖先节点(或者 u 本身)的右儿子。如果 u 没有左儿子,那么下一个遍历的节点就是 u 的右儿子。如果 没有右儿子,我们就会向上回溯,直到遇到第一个有右儿子(且 u 不在它的右儿子的子树中)的节点 ua,那么 v 就是 ua的右儿子。
算法
- 我们用一个栈和一个指针辅助进行二叉树的构造。初始时栈中存放了根节点(前序遍历的第一个节点),指针指向中序遍历的第一个节点;
- 我们依次枚举前序遍历中除了第一个节点以外的每个节点。如果 index 恰好指向栈顶节点,那么我们不断地弹出栈顶节点并向右移动 index,并将当前节点作为最后一个弹出的节点的右儿子;如果 index 和栈顶节点不同,我们将当前节点作为栈顶节点的左儿子;
- 无论是哪一种情况,我们最后都将当前的节点入栈。
最后得到的二叉树即为答案。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| class Solution: def buildTree(self, preorder: List[int], inorder: List[int]) -> TreeNode: if not preorder: return None root = TreeNode(preorder[0]) stack = [root] inorderIndex = 0
for i in range(1,len(preorder)): preorderVal = preorder[i] node = stack[-1]
if node.val != inorder[inorderIndex]: node.left = TreeNode(preorderVal) stack.append(node.left) else: while stack and stack[-1].val == inorder[inorderIndex]: node = stack.pop() inorderIndex += 1
node.right = TreeNode(preorderVal) stack.append(node.right) return root
|