一、题目
注意:
你可以假设树中没有重复的元素。
例如,给出
前序遍历 preorder = [3,9,20,15,7]
中序遍历 inorder = [9,3,15,20,7]
返回如下的二叉树:
3
/ \
9 20
/ \
15 7
二、解决
1、递归
思路:
前序遍历:【根节点】【左子树】【右子树】
中序遍历:【左子树】【根节点】【右子树】
推论:
1、前序遍历首元素为树的根节点node
的值;
2、中序遍历中,搜索node
索引,然后以此为界,可将中序遍历划分为【左子树】【根节点】【右子树】,记录左、右子树的节点数量leftCnt & rightCnt
。
3、根据leftCnt & rightCnt
可以将 前序遍历 划分为【根节点】【左子树】【右子树】。
过程:
- 递推参数:前序遍历索引root、子树在中序遍历左边界left、子树在中序遍历的有边界right;
- 终止条件:left > right,代表越过根节点,此时返回null;
- 递归工作:
3.1. 建立根节点node:节点值为preorder[root];
3.2. 划分左右子树:查找根节点在中序遍历inorder中的索引 i ;
为提升效率,本文使用哈希表dic存储中序遍历的值与索引的映射,查找操作的时间复杂度为O(1);
3.3 构建左右子树:开启左右子树递归
根节点索引 | 中序遍历左边界 | 中序遍历右边界 | |
---|---|---|---|
左子树 | root+1 | left | i-1 |
右子树 | i-left+root+1 | i+1 | right |
代码-版本1:
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
int[] preorder;
HashMap<Integer, Integer> dic = new HashMap<>();
public TreeNode buildTree(int[] preorder, int[] inorder) {
this.preorder = preorder;
for(int i = 0; i < inorder.length; i++)
dic.put(inorder[i], i);
// return recur(0, 0, inorder.length - 1);
// 传入参数:前序,中序,前序序列根节点,中序序列左边界,中序序列右边界
return build(preorder, inorder, 0, 0, inorder.length-1);
}
private TreeNode build (int[] preorder, int[] inorder, int preRoot, int inLeft, int inRight) {
if(inLeft > inRight) return null;
TreeNode root = new TreeNode(preorder[preRoot]);
// 根节点在中序序列中的位置,用于划分左右子树的边界
int inRoot = dic.get(preorder[preRoot]);
// 左子树在前序中的根节点位于:preRoot+1,左子树在中序中的边界:[inLeft, inRight-1]
root.left = build(preorder, inorder, preRoot+1, inLeft, inRoot-1);
// 右子树在前序中的根节点位于:根节点+左子树长度+1 = preRoot+inRoot-inLeft+1
// 右子树在中序中的边界:[inRoot+1,inRight]
root.right = build(preorder, inorder, preRoot+inRoot-inLeft+1, inRoot+1, inRight);
return root;
}
}
代码-版本2: (对版本1 代码进一步精简后)
class Solution {
int[] preorder;
HashMap<Integer, Integer> dic = new HashMap<>();
public