一、题目描述
输入某二叉树的前序遍历和中序遍历的结果,请重建该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。
示例1:
前序遍历:[3,5,7,8,10]
中序遍历:[7,5,3,8,10]
示例图如下:
二、解题思路
前序遍历性质: 节点按照 [ 根节点 | 左子树 | 右子树 ]
排序。
中序遍历性质: 节点按照 [ 左子树 | 根节点 | 右子树 ]
排序。
在二叉树的前序遍历序列中,第一个数字总是树的根节点的值。但在中序遍历序列中,根节点的值在序列的中间,左子树的节点的值位于根节点的值的左边,而右子树的节点的值位于根节点的值的右边。因此我们需要扫描中序遍历序列,才能找到根节点的值。
以上述事例举例:
前序遍历的第一元素一定是根节点,设在前序遍历中根节点(子树的根节点)的索引为preIndex,在中序遍历中的索引为currentIndex,如果存在左子树那么左子树的根节点则一定为preIndex + 1(前序遍历中的索引),不存在则为空节点(null);如果存在右子树则右子树的根节点我们无法根据前序遍历求出。这时候我们就需要根据中序遍历确定右子树的根节点,我们可以根据根节点的左子树的长度,来确定右子树的根节点,在前序遍历中可得右子树的根节点索引为:根节点的索引加上左子树元素的长度再加1.所以就能获取中序遍历该节点索引。
根据如上所述,我门能根据根节点求出左子树根节点和右子树根节点,所以在中序遍历中根据根节点划分两个子树,左子树的根节点和右子树根节点我们已求出,这样把原问题划分两个相同的子问题(分治思想),划分的结束条件是该根节点为叶子结点即可。
为了提升效率,本文使用哈希表 存储中序遍历的值与索引的映射,查找操作的时间复杂度为 O(1)
三、代码实现
public class Solution {
private HashMap<Integer, Integer> posMap = new HashMap<>();
public TreeNode buildTree(int[] preorder, int[] inorder) {
//计算中序遍历元素位置
for(int i = 0; i < inorder.length; i++){
posMap.put(inorder[i],i);
}
return buildBinaryTree(preorder,0,0,inorder.length - 1);
}
/**
*
* @param preorder 前序遍历序列
* @param preIndex 当前子树的根节点索引(前序遍历中的索引)
* @param leftIndex 当前子树的左边界索引(中序遍历中的索引)
* @param rightIndex 当前子树的右边界索引(中序遍历中的索引)
* @return
*/
private TreeNode buildBinaryTree(int[] preorder,int preIndex,int leftIndex,int rightIndex){
if(leftIndex > rightIndex ){
return null;
}
int currentIndex = posMap.get(preorder[preIndex]);
TreeNode root = new TreeNode(preorder[preIndex]);
root.left = buildBinaryTree(preorder,preIndex + 1,leftIndex,currentIndex -1);
root.right = buildBinaryTree(preorder,preIndex + currentIndex - leftIndex + 1,currentIndex +1,rightIndex);
return root;
}
}