Leetcode105. Construct Binary Tree from Preorder and Inorder Traversal
Given preorder and inorder traversal of a tree, construct the binary tree.
Note: You may assume that duplicates do not exist in the tree.
For example, given
preorder = [3,9,20,15,7]
inorder = [9,3,15,20,7]
Return the following binary tree:
3
/ \
9 20
/ \
15 7
思路
前序遍历: 父(根)节点 -> 左子节点 -> 右子节点
中序遍历: 左子节点 -> 父(根)节点 -> 右子节点
后序遍历: 左子节点 -> 右子节点 -> 父(根)节点
结论:前序/后序 + 中序遍历可以确定一棵唯一二叉树。
- 前序中左起第一位 28 肯定是根结点,以此为根据找到中序中根结点的位置。
- 中序中结点分布:[左子树结点,根结点,右子树结点]
- 前序中结点分布:[根结点,左子树结点,右子树结点]
- 确定前序中左子树结点和右子树结点的范围
如果递归生成二叉树,下一层递归应该是
- 左子树:
root->left = buildTree(前序左子树范围,前序起始下标,前序结束下标,中序开始下标);
- 右子树:
root->right = buildTree(前序左子树范围,前序起始下标,前序结束下标,中序开始下标);
两个注意点:
- 每一层递归都要返回当前根结点
root
; - 为了避免在递归过程中线性查找,可以借助哈希表来储存中序的元素与下标
```java
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public TreeNode buildTree(int[] preorder, int[] inorder) {
//借助哈希表来储存二叉树的节点,优化时间复杂度
Map<Integer, Integer> inPos = new HashMap<>();
for (int i = 0; i < inorder.length; ++i){
inPos.put(inorder[i], i);
}
return buildTree(preorder, 0, preorder.length-1, 0, inPos);
}
private TreeNode buildTree( int[] pre, int preStart , int preEnd, int inStart, Map<Integer, Integer> inPos) {
//递归停止条件
if (preStart > preEnd) return null;
//前序中左起第一位肯定是根结点
TreeNode root = new TreeNode(pre[preStart]);
//根结点的位置直接通过中序获取
int rootIdx = inPos.get(pre[preStart]);
//左子树结点个数可以通过中序中根节点的位置与中序中起始位置确定
int leftLen = rootIdx - inStart;
//递归调用
root.left = buildTree(pre, preStart + 1, preStart + leftLen, inStart, inPos);
root.right = buildTree(pre, preStart + leftLen + 1, preEnd, rootIdx + 1, inPos);
return root;
}
}
- 时间复杂度:
O(n)
。 - 空间复杂度:
O(n)
。借助哈希表这种数据结构,需要额外的存储空间,因此空间复杂度为 O(n)。
示例
preorder = [3,9,20,15,7]
inorder = [9,3,15,20,7]
首先根据 preorder 找到根节点是 3
然后根据根节点将 inorder 分成左子树和右子树
左子树
inorder [9]
右子树
inorder [15,20,7]
把相应的前序遍历的数组也加进来
左子树
preorder[9]
inorder [9]
右子树
preorder[20 15 7]
inorder [15,20,7]
现在我们只需要构造左子树和右子树即可,成功把大问题化成了小问题
然后重复上边的步骤继续划分,直到 preorder 和 inorder 都为空,返回 null 即可