题目
给定一棵树的前序遍历 preorder 与中序遍历 inorder。请构造二叉树并返回其根节点。
Input: preorder = [3,9,20,15,7], inorder = [9,3,15,20,7]
Output: [3,9,20,null,null,15,7]
提示:
1 <= preorder.length <= 3000
inorder.length == preorder.length
-3000 <= preorder[i], inorder[i] <= 3000
preorder 和 inorder 均无重复元素
inorder 均出现在 preorder
preorder 保证为二叉树的前序遍历序列
inorder 保证为二叉树的中序遍历序列
分析
这道题最有价值的提示是preorder与inorder的元素均不重复,这对我们加速非常有帮助,否则每次确定位置时需要遍历整个数组。
解决这道问题的关键在于理解前、中、后序遍历的特点,它们都会有一个共同点:某个节点的左子树会连续地出现在数组中,右子树也会连续地出现在数组中,如下图:
从上图可以看出,三种遍历方法得到左子树都是一样长的,右子树也都一样长的,只不过它们的内部顺序不一样而已,一样的长度就成了我们的解题的关键。
前序遍历得到的数组有一个特点,就是第一个元素是根节点,包括它的左右子树都有这样的特点,这样可以通过递归来不停地处理它的子树,但如何确定左右子树的边界呢?
这个时候需要利用到中序遍历了,因为中序遍历的根节点刚好在左右子树的中间,通过这个特点可以分别算出左右子树的长度,这个长度用来帮助前序来确定左右子树的边界了。
代码
class Solution {
public TreeNode buildTree(int[] preorder, int[] inorder) {
Map<Integer, Integer> map = new HashMap<Integer, Integer>();
for(int i=0;i<inorder.length;i++){
map.put(inorder[i], i);
}
TreeNode root = new TreeNode(preorder[0]);
recursive(0, preorder.length-1, 0, preorder.length-1, preorder, root, map);
return root;
}
void recursive(int start, int end, int inStart, int inEnd, int[] preorder, TreeNode parent, Map<Integer, Integer> map) {
int treeRootIndex = map.get(preorder[start]);
int leftTreeLen = treeRootIndex - inStart;
if(leftTreeLen > 0){
TreeNode leftTree = new TreeNode(preorder[start+1]);
parent.left = leftTree;
recursive(start+1, start+leftTreeLen, inStart, treeRootIndex-1, preorder, leftTree, map);
}
int rightTreeLen = inEnd - treeRootIndex;
if(rightTreeLen > 0){
TreeNode rightTree = new TreeNode(preorder[start+1+leftTreeLen]);
parent.right = rightTree;
recursive(start+1+leftTreeLen, end, treeRootIndex+1, inEnd, preorder, rightTree, map);
}
}
}
结果
关注个人微信公众号:肌肉码农,回复“好友”讨论问题。