105. 从前序与中序遍历序列构造二叉树

158 阅读3分钟

思路

  • 前序遍历的形式总是,[ 根节点, [左子树的前序遍历结果], [右子树的前序遍历结果] ]

  • 中序遍历的形式总是,[ [左子树的中序遍历结果], 根节点, [右子树的中序遍历结果] ]

  • 所以,

    • 根据前序遍历头元素找出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;
    }
}