输入某二叉树的前序遍历和中序遍历的结果,请重建该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。
例如,给出
前序遍历 preorder = [3,9,20,15,7]
中序遍历 inorder = [9,3,15,20,7]
返回如下的二叉树:
3
/
9 20
/
15 7
思路:
首先我们要知道二叉树遍历方式的顺序:
- 前序遍历:根节点—左子节点—右子节点
- 中序遍历:左子节点—根节点—右子节点
- 后序遍历:左子节点—右子节点—根节点
我们就以上面的示例数据来看下,前序遍历是[3,9,20,15,7],前序遍历先访问的是根节点,所以3就是根节点。中序遍历是[9,3,15,20,7],由于中序遍历是在左子树都遍历完的时候才遍历根节点,所有在中序遍历中3前面的都是3的左子树节点,3后面的都是3的右子树节点,具体如图所示:
然后我们再对左右子树进行不断一直划分,知道不能划分下去
题解:
这里我们需要三个指针
- root:节点在前需遍历中的索引
- left:节点在中序遍历开始的位置
- right:节点在中序遍历结束的位置
我们主要是对中序遍历的数组进行拆分,以下面这个树画图进行拆分下:
他的前序遍历是:[3,9,8,5,2,20,15,7]
他的中序遍历是:[5,8,9,2,3,15,20,7]
进行拆分后:
因为我们前需遍历的节点就是根节点位置,所以我们需要遍历根节点,但同时我们需要知道前序遍历的节点在根节点的位置对数组进行拆分,所以这里我们用一个map将中序遍历的节点作为key,在数组中的索引作为value,保存在map中,这样前序遍历的节点在中序遍历数组中的位置就很方便得到
这样当i是某个前序遍历的节点在中序遍历的位置时候,以i为根节点进行划分,[0,i-1]是根节点左子树的所有节点,[i+1,inorder.length-1]就是根节点右子树的所有节点
中序遍历比较容易划分,那么前序遍历呢,左子树:
left=root+1;
右子树比较麻烦一点:
right=root+i-left+1
这里i-left+1
指的是当前左子节点的数量加上当前节点的数量,所以:root+i-left+1
就是当前节点右子树前序遍历开始的位置
代码:
class solution {
int[] preorder;
Map<Integer, Integer> map = new HashMap();
public TreeNode buildTree(int[] preorder, int[] inorder) {
this.preorder = preorder;
for (int i = 0; i < inorder.length; i++) {
map.put(inorder[i], i);
}
return recur(0, 0, inorder.length - 1);
}
/**
* 递归建立树
*
* @param root 节点在前序遍历的索引
* @param left 节点在中序遍历左边的索引
* @param right 节点在中序遍历右边的索引
* @return
*/
TreeNode recur(int root, int left, int right) {
if (left > right) return null; //递归终止条件
TreeNode node = new TreeNode(preorder[root]); //建立根节点
int i = map.get(preorder[root]); //根据根节点在中序遍历的索引进行划分左右子树
node.left = recur(root + 1, left, i - 1); //左子树递归
node.right = recur(root + i - left + 1, i + 1, right); //右子树递归
return node;
}
}
class TreeNode {
int val;
TreeNode left;
TreeNode right;
TreeNode(int x) {
val = x;
}
}