105. Construct Binary Tree from Preorder and Inorder Traversal

First Edition

We use leftLen to represent the size of the left sub-tree, which helps us to understand how to calculate preStart and preEnd of the left sub-tree and right sub-tree.

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 * int val;
 * TreeNode left;
 * TreeNode right;
 * TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public TreeNode buildTree(int[] preorder, int[] inorder) {
        if (preorder.length != inorder.length) {
            return null;
        }
        return helper(0, preorder.length - 1, 0, inorder.length - 1, preorder, inorder);
    }

    private TreeNode helper(int preStart, int preEnd, int inStart, int inEnd, 
                            int[] preorder, int[] inorder) {
        // base cases
        if (preStart > preEnd || inStart > inEnd) {
            return null;
        }

        // the current sub-tree root
        TreeNode root = new TreeNode(preorder[preStart]);

        // find the current root index in inorder array
        int inRoot = inStart;
        for (int i = inStart; i <= inEnd; i++) {
            if (inorder[i] == root.val) {
                inRoot = i;
                break;
            }
        }

        // the node number of the left sub-tree
        int leftLen = inRoot - inStart;
        
        root.left = helper(preStart + 1, preStart + leftLen, inStart, inRoot - 1, 
                           preorder, inorder);
        root.right = helper(preStart + leftLen + 1, preEnd, inRoot + 1, preEnd, 
                            preorder, inorder);

        return root;
    }
}

Second Edition

In the first edition, we use linear scan to find the current root index, which is very time consuming. Thus we can use hashmap to cache the inorder[i] -> i mapping to optimize the code.

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 * int val;
 * TreeNode left;
 * TreeNode right;
 * TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public TreeNode buildTree(int[] preorder, int[] inorder) {
        if (preorder.length != inorder.length) {
            return null;
        }
        
        // cache
        Map<Integer, Integer> mapping = new HashMap<>(inorder.length);
        for (int i = 0; i < inorder.length; i++) {
            mapping.put(inorder[i], i);
        }
        
        return helper(0, preorder.length - 1, 0, inorder.length - 1,
                preorder, inorder, mapping);
    }

    private TreeNode helper(int preStart, int preEnd, int inStart, int inEnd,
                            int[] preorder, int[] inorder, Map<Integer, Integer> mapping) {
        // base cases
        if (preStart > preEnd || inStart > inEnd) {
            return null;
        }

        // the current sub-tree root
        TreeNode root = new TreeNode(preorder[preStart]);

        // find the current root index in inorder array
        int inRoot = mapping.get(root.val);

        // the node number of the left sub-tree
        int leftLen = inRoot - inStart;
        
        root.left = helper(preStart + 1, preStart + leftLen, inStart, inRoot - 1, 
                           preorder, inorder, mapping);
        root.right = helper(preStart + leftLen + 1, preEnd, inRoot + 1, preEnd, 
                            preorder, inorder, mapping);

        return root;
    }
}