Construct Binary Tree from Preorder and Inorder Traversal
MedPrompt
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.
Example 1:
Input: preorder = [3,9,20,15,7], inorder = [9,3,15,20,7]
Output: [3,9,20,null,null,15,7]Example 2:
Input: preorder = [-1], inorder = [-1]
Output: [-1]
Constraints:
1 <= preorder.length <= 3000inorder.length == preorder.length-3000 <= preorder[i], inorder[i] <= 3000preorderandinorderconsist of unique values.- Each value of
inorderalso appears inpreorder. preorderis guaranteed to be the preorder traversal of the tree.inorderis guaranteed to be the inorder traversal of the tree.
Approaches
3 approaches with complexity analysis and trade-offs.
This approach directly translates the properties of preorder and inorder traversals into a recursive algorithm. The first element of the preorder array is the root. We find this root in the inorder array to determine the elements of the left and right subtrees. Then, we create new subarrays for the preorder and inorder traversals of the left and right subtrees and make recursive calls.
Algorithm
- If the
preorderarray is empty, returnnull. - The first element of the
preorderarray,preorder[0], is the root of the tree. Create aTreeNodefor this root. - Find the index,
mid, of the root's value in theinorderarray. This is a linear scan. - The elements to the left of
midin theinorderarray form the left subtree. The elements to the right form the right subtree. - The number of nodes in the left subtree is
mid. - Create new subarrays for the left and right subtrees:
left_inorder:inorderfrom index0tomid - 1.right_inorder:inorderfrom indexmid + 1to the end.left_preorder:preorderfrom index1tomid.right_preorder:preorderfrom indexmid + 1to the end.
- Recursively call the function to build the left child:
root.left = buildTree(left_preorder, left_inorder). - Recursively call the function to build the right child:
root.right = buildTree(right_preorder, right_inorder). - Return the
rootnode.
Walkthrough
The core idea is to use recursion. The base case for the recursion is when the input arrays are empty, in which case we return null.
In each recursive step:
- Pick the first element from the
preorderarray. This is the root of the current subtree. - Create a new
TreeNodewith this value. - Search for this root's value in the
inorderarray. Let's say its index isk. - All elements in the
inorderarray to the left ofk(from index 0 tok-1) belong to the left subtree. - All elements in the
inorderarray to the right ofk(from indexk+1to the end) belong to the right subtree. - The number of elements in the left subtree is
k. - The next
kelements in thepreorderarray (after the root) belong to the left subtree's preorder traversal. The rest belong to the right subtree's preorder traversal. - Create new subarrays for the left and right subtrees' traversals (
left_inorder,right_inorder,left_preorder,right_preorder). - Recursively call the function to build the left and right children.
This process is simple to understand but inefficient due to the repeated creation of new arrays and linear scanning in each recursive call.
import java.util.Arrays; /** * Definition for a binary tree node. * public class TreeNode { * int val; * TreeNode left; * TreeNode right; * TreeNode() {} * TreeNode(int val) { this.val = val; } * TreeNode(int val, TreeNode left, TreeNode right) { * this.val = val; * this.left = left; * this.right = right; * } * } */class Solution { public TreeNode buildTree(int[] preorder, int[] inorder) { if (preorder == null || preorder.length == 0) { return null; } // The first element of preorder is the root TreeNode root = new TreeNode(preorder[0]); // Find the root in the inorder array int mid = -1; for (int i = 0; i < inorder.length; i++) { if (inorder[i] == root.val) { mid = i; break; } } // Create subarrays for left and right subtrees int[] leftInorder = Arrays.copyOfRange(inorder, 0, mid); int[] rightInorder = Arrays.copyOfRange(inorder, mid + 1, inorder.length); int[] leftPreorder = Arrays.copyOfRange(preorder, 1, mid + 1); int[] rightPreorder = Arrays.copyOfRange(preorder, mid + 1, preorder.length); // Recursively build the left and right subtrees root.left = buildTree(leftPreorder, leftInorder); root.right = buildTree(rightPreorder, rightInorder); return root; }}Complexity
Time
O(N^2)
Space
O(N^2)
Trade-offs
Pros
Conceptually simple and easy to understand.
Directly follows the definition of the traversals.
Cons
Highly inefficient in both time and space due to the creation of new array copies in every recursive call.
The repeated linear scan to find the root's index in the inorder array makes it slow for large inputs.
Solutions
Solution
/** * Definition for a binary tree node. * public class TreeNode { * int val; * TreeNode left; * TreeNode right; * TreeNode() {} * TreeNode(int val) { this.val = val; } * TreeNode(int val, TreeNode left, TreeNode right) { * this.val = val; * this.left = left; * this.right = right; * } * } */ class Solution { private int [] preorder ; private Map < Integer , Integer > d = new HashMap <>(); public TreeNode buildTree ( int [] preorder , int [] inorder ) { int n = preorder . length ; this . preorder = preorder ; for ( int i = 0 ; i < n ; ++ i ) { d . put ( inorder [ i ], i ); } return dfs ( 0 , 0 , n ); } private TreeNode dfs ( int i , int j , int n ) { if ( n <= 0 ) { return null ; } int v = preorder [ i ]; int k = d . get ( v ); TreeNode l = dfs ( i + 1 , j , k - j ); TreeNode r = dfs ( i + 1 + k - j , k + 1 , n - 1 - ( k - j )); return new TreeNode ( v , l , r ); } }Video walkthrough
Newsletter
One sharp idea, every week
System design and interview prep — short enough to finish.
No spam. Unsubscribe anytime.
Practice
Same difficulty — related problems to reinforce the pattern.