思路
-
前序遍历的形式总是,
[ 根节点, [左子树的前序遍历结果], [右子树的前序遍历结果] ] -
中序遍历的形式总是,
[ [左子树的中序遍历结果], 根节点, [右子树的中序遍历结果] ] -
所以,
- 根据前序遍历头元素找出root
- 确定root在中序数组的位置,进而确定左右子树的前、中序数组。
- 递归找出的左右子树的前、中序数组,添加左右子树的root到本层的root上。
方法二:优化,Map定位,递归参数传原始数组和新index范围
方法一 每次递归copy空间消耗太大;root的定位为O(N),可以用map优化为O(1)。递归函数的参数传index范围,不传int[],可以解决这两个问题
class Solution {
Map<Integer, Integer> map = new HashMap<>();
public TreeNode buildTree(int[] preorder, int[] inorder) {
int length = inorder.length;
//特判
if (length == 0) {
return null;
}
//map来记录root在中序遍历数组中的index
for (int i = 0; i < length; i++) {
map.put(inorder[i], i);
}
return build(preorder, inorder, 0, length - 1, 0, length - 1);
}
//preLeft:当前前序遍历数组的最左边元素在初始数组preorder中的索引
//preRight:当前前序遍历数组的最右边元素在初始数组preorder中的索引
//inLeft:当前中序遍历数组的最左边元素在初始数组inorder中的索引
//inRight:当前中序遍历数组的最右边元素在初始数组inorder中的索引
public TreeNode build(int[] preorder, int[] inorder, int preLeft, int preRight, int inLeft, int inRight) {
//终止条件:区间长度<1
if (preRight - preLeft < 0) {//或者 inRight - inLeft < 0
return null;
}
TreeNode root = new TreeNode(preorder[preLeft]);//不再是preorder[0]
//在中序遍历数组中定位root,O(1),返回其索引
int index = map.get(preorder[preLeft]);
//递归build右子树,确定前序、中序数组的索引范围,由left、right控制
TreeNode right = build(preorder, inorder, index - inLeft + preLeft + 1, preRight, index + 1, inRight);
//递归build右子树,确定前序、中序数组的索引范围,由left、right控制
TreeNode left = build(preorder, inorder, preLeft + 1, preLeft + index - inLeft, inLeft, index - 1);
//得到左右子树构造结果,构造本层树
root.left = left;
root.right = right;
//构造完成,返回root
return root;
}
}
方法一: O(N)遍历定位,递归传arrcopy的新数组
class Solution {
public TreeNode buildTree(int[] preorder, int[] inorder) {
int length = inorder.length;
//特判
if (length == 0) {
return null;
}
//根节点即为前序遍历数组的首元素
TreeNode root = new TreeNode(preorder[0]);
//终止条件:长度=1
if (length == 1) {
return root;
}
//i来记录root在中序遍历数组中的index
int i = 0;
//在中序遍历数组中定位root,O(n),可以优化
for (i = 0; i < length; i++) {
if (inorder[i] == preorder[0]) {
break;//inorder中找到了root
}
}
//确定左右子树的中序遍历数组
int[] inRight = new int[length - 1 - i];
System.arraycopy(inorder, i + 1, inRight, 0, length - 1 - i);
int[] inLeft = new int[i];
System.arraycopy(inorder, 0, inLeft, 0, i);
//确定左右子树的前序遍历数组
int[] preLeft = new int[i];
System.arraycopy(preorder, 1, preLeft, 0, i);
int[] preRight = new int[length - 1 - i];
System.arraycopy(preorder, i + 1, preRight, 0, length - i - 1);//copy长度别写成length-i
//递归build左右子树
TreeNode right = buildTree(preRight, inRight);
TreeNode left = buildTree(preLeft, inLeft);
//得到左右子树构造结果,构造本层树
root.left = left;
root.right = right;
//构造完成,返回root
return root;
}
}