105. 从前序与中序遍历序列构造二叉树
根据一棵树的前序遍历与中序遍历构造二叉树。
注意:
你可以假设树中没有重复的元素。
例如,给出
前序遍历 preorder = [3,9,20,15,7]
中序遍历 inorder = [9,3,15,20,7]
返回如下的二叉树:
(来源:力扣(LeetCode))
思路:
前序遍历的顺序是 [根节点],[左子树的前序遍历结果],[右子树的前序遍历结果]。
中序遍历的顺序是[左子树的中序遍历结果], [根节点], [右子树的中序遍历结果]。
所以前序遍历的第一个结点一定是根结点,只要我们在中序遍历中定位到根节点,那么就可以分别知道左子树和右子树中的节点数目。同一颗子树的前序遍历和中序遍历的长度是相同的,因此我们就可以对应到前序遍历的结果中。所以我们就可以计算出前序遍历结果和中序遍历结果中根结点、左子树区间、右子树区间的下标值。
首先,设置前序遍历子区间的左边界和右边界分别为preLeft和preRight;中序遍历子区间的左边界和右边界分别为inLeft和inRight,且根结点下标为pIndex。
然后,在前序遍历中左子树区间的左边界为preLeft + 1;中序遍历中左子树区间的右边界为pIndex - 1,右子树区间的左边界为pIndex + 1,。
接着,计算前序遍历中左子树区间和右子树区间的分界限。因为前序遍历和中序遍历中左子树结点数和右子树结点数是一样的,所以,假设前序遍历中左子树区间的右边界为x,
pIndex - 1 - inLeft = x - (preLeft + 1), 得x = pIndex + preLeft - inLeft,
即前序遍历中左子树区间的右边界为pIndex + preLeft - inLeft,也可得前序遍历中右子树区间的左边界为pIndex + preLeft - inLeft + 1。
由此,下标已经全部清晰了。
我们可以用HashMap来记录中序遍历中数值和下标的对应关系,key表示节点的值,value表示其在中序遍历中的出现位置,这样我们每次都可以根据前序遍历里的数值找到这个值在中序遍历里的下标。
class Solution {
public Map<Integer, Integer> map;
public TreeNode buildTree(int[] preorder, int[] inorder) {
map = new HashMap<Integer, Integer>();
int n = preorder.length;
for (int i = 0; i < n; i++) {
map.put(inorder[i],i);
}
return traverse(preorder, inorder, 0, n - 1, 0, n - 1);
}
public TreeNode traverse(int[] preorder, int[] inorder, int preLeft, int preRight,
int inLeft, int inRight) {
if (preLeft > preRight) { // 递归终止条件
return null;
}
int preorder_root = preLeft;
//定位中序遍历中的根结点
int pIndex = map.get(preorder[preorder_root]);
//创建根结点
TreeNode root = new TreeNode(preorder[preorder_root]);
//得到左子树的结点数目
int size_left = pIndex - inLeft;
//递归构造左子树,并连接根结点
root.left = traverse(preorder, inorder, preLeft + 1, preLeft + size_left, inLeft, pIndex - 1);
root.right = traverse(preorder, inorder, preLeft + size_left + 1, preRight, pIndex + 1,inRight);
return root;
}
}