Construct Binary Tree from Preorder and Inorder Traversal

medium

Given two integer arrays preorder and inorder where preorder is the preorder traversal of a binary tree and inorder is the inorder traversal of the same tree, construct and return the binary tree.

Examples

Example 1

  • Input: preorder = [3,9,20,15,7], inorder = [9,3,15,20,7]
  • Output: [3,9,20,null,null,15,7]
  • Explanation: The preorder traversal visits root first (3), then left subtree (9), then right subtree (20, 15, 7). The inorder traversal shows left subtree (9), root (3), then right subtree (15, 20, 7).

Example 2

  • Input: preorder = [-1], inorder = [-1]
  • Output: [-1]

Constraints

  • 1 <= preorder.length <= 3000
  • inorder.length == preorder.length
  • -3000 <= preorder[i], inorder[i] <= 3000
  • preorder and inorder consist of unique values.
  • Each value of inorder also appears in preorder.
  • preorder is guaranteed to be the preorder traversal of the tree.
  • inorder is guaranteed to be the inorder traversal of the tree.

Approaches

2 worked solutions ranked from straightforward to optimal — each with intuition, algorithm, complexity, and verified code in C++, Java, Python, and Go.

Approach 1

Recursive with HashMap

Intuition

The first element in the preorder array is always the root of the current subtree. By finding this root's position in the inorder array, we can determine which elements belong to the left subtree (everything before the root in inorder) and which belong to the right subtree (everything after). We use a hash map to look up positions in the inorder array in O(1) time, and a pointer to track our position in the preorder array. The left subtree is built first because preorder visits left before right.

Algorithm

  1. Build a hash map from each value in inorder to its index for O(1) lookup.
  2. Initialize a preorder index pointer starting at 0.
  3. Define a recursive function that takes the current inorder range (left, right).
  4. If left > right, return null (empty subtree).
  5. The current root value is preorder[preorderIndex]. Increment the pointer.
  6. Create a new node with this value.
  7. Find the root's index in inorder using the hash map.
  8. Recursively build the left subtree with the inorder range (left, rootIndex - 1).
  9. Recursively build the right subtree with the inorder range (rootIndex + 1, right).
  10. Return the node.
TimeO(n)SpaceO(n)

Code (C++ · Java · Python · Go)

C++17
class Solution {
public:
    TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
        unordered_map<int, int> inorderMap;
        for (int i = 0; i < inorder.size(); i++) {
            inorderMap[inorder[i]] = i;
        }
        int preIdx = 0;
        return build(preorder, inorderMap, preIdx, 0, inorder.size() - 1);
    }

private:
    TreeNode* build(vector<int>& preorder, unordered_map<int, int>& inorderMap,
                    int& preIdx, int left, int right) {
        if (left > right) return nullptr;
        int rootVal = preorder[preIdx++];
        TreeNode* root = new TreeNode(rootVal);
        int mid = inorderMap[rootVal];
        root->left = build(preorder, inorderMap, preIdx, left, mid - 1);
        root->right = build(preorder, inorderMap, preIdx, mid + 1, right);
        return root;
    }
};
Java 21
class Solution {
    private int preIdx = 0;
    private Map<Integer, Integer> inorderMap = new HashMap<>();

    public TreeNode buildTree(int[] preorder, int[] inorder) {
        for (int i = 0; i < inorder.length; i++) {
            inorderMap.put(inorder[i], i);
        }
        return build(preorder, 0, inorder.length - 1);
    }

    private TreeNode build(int[] preorder, int left, int right) {
        if (left > right) return null;
        int rootVal = preorder[preIdx++];
        TreeNode root = new TreeNode(rootVal);
        int mid = inorderMap.get(rootVal);
        root.left = build(preorder, left, mid - 1);
        root.right = build(preorder, mid + 1, right);
        return root;
    }
}
Python 3.12
def buildTree(preorder: list[int], inorder: list[int]) -> Optional[TreeNode]:
    inorder_map = {val: idx for idx, val in enumerate(inorder)}
    pre_idx = [0]

    def build(left, right):
        if left > right:
            return None
        root_val = preorder[pre_idx[0]]
        pre_idx[0] += 1
        root = TreeNode(root_val)
        mid = inorder_map[root_val]
        root.left = build(left, mid - 1)
        root.right = build(mid + 1, right)
        return root

    return build(0, len(inorder) - 1)
Go 1.26
func buildTree(preorder []int, inorder []int) *TreeNode {
    inorderMap := make(map[int]int)
    for i, v := range inorder {
        inorderMap[v] = i
    }
    preIdx := 0
    var build func(int, int) *TreeNode
    build = func(left, right int) *TreeNode {
        if left > right {
            return nil
        }
        rootVal := preorder[preIdx]
        preIdx++
        root := &TreeNode{Val: rootVal}
        mid := inorderMap[rootVal]
        root.Left = build(left, mid-1)
        root.Right = build(mid+1, right)
        return root
    }
    return build(0, len(inorder)-1)
}
Approach 2

Recursive with Array Slicing

Intuition

A more intuitive but less efficient approach uses array slicing instead of index tracking. The first element of preorder is the root. We find this value in inorder to determine the sizes of the left and right subtrees. We then slice both arrays accordingly and recurse. The left subtree gets the next leftSize elements from preorder and the elements before root in inorder. The right subtree gets the remaining elements. While clearer to understand, this approach creates new arrays at each level.

Algorithm

  1. If the preorder array is empty, return null.
  2. The first element of preorder is the root value. Create the root node.
  3. Find the index of the root value in inorder.
  4. Elements in inorder before this index form the left subtree; elements after form the right subtree.
  5. The next leftSize elements in preorder (after the root) correspond to the left subtree.
  6. The remaining elements in preorder correspond to the right subtree.
  7. Recursively build the left and right subtrees with the appropriate slices.
TimeO(n^2) due to array slicing and linear searchSpaceO(n^2) due to array copies

Code (C++ · Java · Python · Go)

C++17
class Solution {
public:
    TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
        if (preorder.empty()) return nullptr;
        int rootVal = preorder[0];
        TreeNode* root = new TreeNode(rootVal);
        int mid = find(inorder.begin(), inorder.end(), rootVal) - inorder.begin();
        vector<int> leftPre(preorder.begin() + 1, preorder.begin() + 1 + mid);
        vector<int> rightPre(preorder.begin() + 1 + mid, preorder.end());
        vector<int> leftIn(inorder.begin(), inorder.begin() + mid);
        vector<int> rightIn(inorder.begin() + mid + 1, inorder.end());
        root->left = buildTree(leftPre, leftIn);
        root->right = buildTree(rightPre, rightIn);
        return root;
    }
};
Java 21
class Solution {
    public TreeNode buildTree(int[] preorder, int[] inorder) {
        if (preorder.length == 0) return null;
        int rootVal = preorder[0];
        TreeNode root = new TreeNode(rootVal);
        int mid = 0;
        for (int i = 0; i < inorder.length; i++) {
            if (inorder[i] == rootVal) {
                mid = i;
                break;
            }
        }
        root.left = buildTree(
            Arrays.copyOfRange(preorder, 1, mid + 1),
            Arrays.copyOfRange(inorder, 0, mid)
        );
        root.right = buildTree(
            Arrays.copyOfRange(preorder, mid + 1, preorder.length),
            Arrays.copyOfRange(inorder, mid + 1, inorder.length)
        );
        return root;
    }
}
Python 3.12
def buildTree(preorder: list[int], inorder: list[int]) -> Optional[TreeNode]:
    if not preorder:
        return None
    root_val = preorder[0]
    root = TreeNode(root_val)
    mid = inorder.index(root_val)
    root.left = buildTree(preorder[1:mid + 1], inorder[:mid])
    root.right = buildTree(preorder[mid + 1:], inorder[mid + 1:])
    return root
Go 1.26
func buildTree(preorder []int, inorder []int) *TreeNode {
    if len(preorder) == 0 {
        return nil
    }
    rootVal := preorder[0]
    root := &TreeNode{Val: rootVal}
    mid := 0
    for i, v := range inorder {
        if v == rootVal {
            mid = i
            break
        }
    }
    root.Left = buildTree(preorder[1:mid+1], inorder[:mid])
    root.Right = buildTree(preorder[mid+1:], inorder[mid+1:])
    return root
}

Frequently asked questions

What is the optimal time complexity of Construct Binary Tree from Preorder and Inorder Traversal?

The most efficient approach in this guide ("Recursive with Array Slicing") runs in O(n^2) due to array slicing and linear search time and uses O(n^2) due to array copies extra space. Earlier brute-force approaches are slower; see all 2 approaches above for the full progression.

What pattern does Construct Binary Tree from Preorder and Inorder Traversal use?

Construct Binary Tree from Preorder and Inorder Traversal is a medium-level Trees problem involving Tree, Binary Tree, Divide and Conquer. Recognizing the pattern is half the work — see the intuition section above for how to spot it.

Can Construct Binary Tree from Preorder and Inorder Traversal be solved without extra space?

The most space-efficient approach in this guide uses O(n^2) due to array copies extra space (excluding the input). If you're aiming for in-place — see the trade-off in the algorithm section above — the brute-force approach uses O(n), while the optimized version uses O(n^2) due to array copies.

Which programming languages does this Ratta solution support?

Every approach above ships with verified, runnable solutions in C++, Java, Python, and Go. The starter templates in the workspace match the same four languages so you can practice in your interview language of choice.

Original problem: leetcode.com

Loading editor...
preorder=
[3,9,20,15,7]
inorder=
[9,3,15,20,7]
[3,9,20,null,null,15,7]