Skip to content

Construct Binary Tree from Preorder and Inorder Traversal

Given two integer arrays preorder and inorder where:

  • preorder is the preorder traversal of a binary tree
  • inorder is the inorder traversal of the same tree

Reconstruct and return the binary tree.

Preorder traversal visits nodes in this order: Root → Left → Right

Inorder traversal visits nodes in this order: Left → Root → Right

PreorderInorderTree Structure
[3, 9, 20, 15, 7][9, 3, 15, 20, 7]Root=3, Left child=9, Right child=20 (with its own subtree)
[1][1]Single node tree
[1, 2, 3][1, 3, 2]Root=1, Right child=2, Left child of 2 is 3
  • 1 <= preorder.length <= 3000
  • inorder.length == preorder.length
  • -3000 <= preorder[i], inorder[i] <= 3000
  • preorder and inorder consist of unique values.
  • Each value of preorder also appears in inorder.
  • inorder does not contain duplicates.
  • preorder does not contain duplicates.
ApproachTimeSpaceLookupBest When
Hash MapO(n)O(n)Instant O(1)General case — predictable performance
Index TrackingO(n²) worstO(h)O(n) linear searchMemory-constrained, or when inorder lookups are rare
Iterative with StackO(n)O(h)Instant O(1)Advanced optimization
  • Pick Hash Map if you want the clearest, most balanced approach.
  • Pick Index Tracking if you want to minimize extra space and understand the basic recursion pattern.
Best for Speed
Hash Map
O(n) time · O(n) space
Space-Efficient
Index Tracking
O(n²) worst · O(h) space
★ Recommended

Store the position of each value in inorder in a hash map for instant lookup. For each node in preorder, find its position in inorder to determine the split between left and right subtrees.

The key insight: once you know the root (from preorder), you can instantly find its position in inorder using the hash map, then partition the arrays and recurse.

⏱ Time O(n) Single pass per element 💾 Space O(n) Hash map + recursion stack

Preorder: [3, 9, 20, 15, 7]

Inorder: [9, 3, 15, 20, 7]

Hash Map: {9: 0, 3: 1, 15: 2, 20: 3, 7: 4}

StepRootPosition in InorderLeft ElementsRight ElementsAction
13 (preorder[0])1[9] at inorder[0:1][15, 20, 7] at inorder[2:5]Split at position 1
29 (preorder[1])0[][]Leaf node
320 (preorder[2])3[15] at inorder[2:3][7] at inorder[4:5]Split at position 3

Animated walkthrough

How the reconstruction algorithm finds the tree structure

Watch as the preorder root pointer moves forward and the inorder boundaries shrink during recursion.

Step 1 of 3 Target: root
Waiting to begin...
Array state
Memory / lookup state

function buildTreeHashMap(preorder, inorder):
if preorder is empty or inorder is empty:
return null
// Create hash map for O(1) lookup
inorder_map = {}
for i, val in enumerate(inorder):
inorder_map[val] = i
function build(preorder_start, preorder_end, inorder_start, inorder_end):
if preorder_start > preorder_end or inorder_start > inorder_end:
return null
root_val = preorder[preorder_start]
root = new TreeNode(root_val)
// Find root position in inorder
root_inorder_idx = inorder_map[root_val]
// Calculate left subtree size
left_size = root_inorder_idx - inorder_start
// Recurse on left subtree
root.left = build(
preorder_start + 1,
preorder_start + left_size,
inorder_start,
root_inorder_idx - 1
)
// Recurse on right subtree
root.right = build(
preorder_start + left_size + 1,
preorder_end,
root_inorder_idx + 1,
inorder_end
)
return root
return build(0, len(preorder) - 1, 0, len(inorder) - 1)
construct_binary_tree_from_preorder_and_inorder_traversal_hash_map.py
from typing import Optional, List, Dict
class TreeNode:
def __init__(self, val: int = 0, left: Optional['TreeNode'] = None, right: Optional['TreeNode'] = None):
self.val = val
self.left = left
self.right = right
def buildTree_hash_map(preorder: List[int], inorder: List[int]) -> Optional[TreeNode]:
"""
Construct a binary tree from preorder and inorder traversal using hash map.
Time: O(n), Space: O(n)
"""
if not preorder or not inorder:
return None
# Create a map for quick lookup of indices in inorder
inorder_map: Dict[int, int] = {val: i for i, val in enumerate(inorder)}
def build(preorder_start: int, preorder_end: int, inorder_start: int, inorder_end: int) -> Optional[TreeNode]:
if preorder_start > preorder_end or inorder_start > inorder_end:
return None
# Root is the first element in preorder
root_val = preorder[preorder_start]
root = TreeNode(root_val)
# Find root position in inorder
root_inorder_idx = inorder_map[root_val]
# Number of elements in left subtree
left_size = root_inorder_idx - inorder_start
# Build left subtree from remaining preorder and inorder
root.left = build(
preorder_start + 1,
preorder_start + left_size,
inorder_start,
root_inorder_idx - 1
)
# Build right subtree
root.right = build(
preorder_start + left_size + 1,
preorder_end,
root_inorder_idx + 1,
inorder_end
)
return root
return build(0, len(preorder) - 1, 0, len(inorder) - 1)
# Test cases
if __name__ == "__main__":
# Test 1: [3,9,20,15,7], [9,3,15,20,7]
preorder1 = [3, 9, 20, 15, 7]
inorder1 = [9, 3, 15, 20, 7]
root1 = buildTree_hash_map(preorder1, inorder1)
print(f"Test 1 - Root: {root1.val}") # 3
# Test 2: [1], [1]
preorder2 = [1]
inorder2 = [1]
root2 = buildTree_hash_map(preorder2, inorder2)
print(f"Test 2 - Root: {root2.val}") # 1
# Test 3: [1,2,3], [1,3,2]
preorder3 = [1, 2, 3]
inorder3 = [1, 3, 2]
root3 = buildTree_hash_map(preorder3, inorder3)
print(f"Test 3 - Root: {root3.val}, Left: {root3.left.val}, Right: {root3.right.val}") # 1, 2, 3
🎯 Interview Favourite

Instead of storing a hash map, use a pointer to track the current position in preorder, and search for the root in inorder manually during each recursion. This saves the O(n) space used by the hash map, but trades it for slower inorder lookups.

This approach is useful when space is severely limited or when you want to demonstrate understanding of the core recursion pattern without relying on extra data structures.

⏱ Time O(n²) Linear inorder search per node 💾 Space O(h) Recursion stack height

Preorder: [3, 9, 20, 15, 7]

Inorder: [9, 3, 15, 20, 7]

Preorder pointer: Advances left-to-right through preorder

StepPreorder PointerCurrent ValueInorder BoundariesAction
103[0:5] → find 3 at 1Split: left [0:1], right [2:5]
219[0:1] → find 9 at 0Leaf node
3220[2:5] → find 20 at 3Split: left [2:3], right [4:5]
function buildTreeIndex(preorder, inorder):
if preorder is empty or inorder is empty:
return null
preorder_idx = 0
function build(inorder_start, inorder_end):
if inorder_start > inorder_end or preorder_idx >= len(preorder):
return null
root_val = preorder[preorder_idx]
preorder_idx++
root = new TreeNode(root_val)
// Find root position in inorder by linear search
root_inorder_idx = search(inorder, root_val, inorder_start, inorder_end)
// Recurse on left subtree
root.left = build(inorder_start, root_inorder_idx - 1)
// Recurse on right subtree
root.right = build(root_inorder_idx + 1, inorder_end)
return root
return build(0, len(inorder) - 1)
construct_binary_tree_from_preorder_and_inorder_traversal_index.py
from typing import Optional, List
class TreeNode:
def __init__(self, val: int = 0, left: Optional['TreeNode'] = None, right: Optional['TreeNode'] = None):
self.val = val
self.left = left
self.right = right
def buildTree_index(preorder: List[int], inorder: List[int]) -> Optional[TreeNode]:
"""
Construct a binary tree from preorder and inorder traversal using index tracking.
Time: O(n), Space: O(h) where h is height (excluding output tree)
"""
if not preorder or not inorder:
return None
self.preorder_idx = 0
def build(inorder_start: int, inorder_end: int) -> Optional[TreeNode]:
if inorder_start > inorder_end or self.preorder_idx >= len(preorder):
return None
# Root is the current element in preorder
root_val = preorder[self.preorder_idx]
self.preorder_idx += 1
root = TreeNode(root_val)
# Find root position in inorder
root_inorder_idx = inorder.index(root_val, inorder_start, inorder_end + 1)
# Build left subtree from inorder[inorder_start : root_inorder_idx]
root.left = build(inorder_start, root_inorder_idx - 1)
# Build right subtree from inorder[root_inorder_idx + 1 : inorder_end + 1]
root.right = build(root_inorder_idx + 1, inorder_end)
return root
return build(0, len(inorder) - 1)
# Test cases
if __name__ == "__main__":
# Test 1: [3,9,20,15,7], [9,3,15,20,7]
preorder1 = [3, 9, 20, 15, 7]
inorder1 = [9, 3, 15, 20, 7]
root1 = buildTree_index(preorder1, inorder1)
print(f"Test 1 - Root: {root1.val}") # 3
# Test 2: [1], [1]
preorder2 = [1]
inorder2 = [1]
root2 = buildTree_index(preorder2, inorder2)
print(f"Test 2 - Root: {root2.val}") # 1
# Test 3: [1,2,3], [1,3,2]
preorder3 = [1, 2, 3]
inorder3 = [1, 3, 2]
root3 = buildTree_index(preorder3, inorder3)
print(f"Test 3 - Root: {root3.val}, Left: {root3.left.val}, Right: {root3.right.val}") # 1, 2, 3
✓ Simple

A third approach demonstrating an alternative technique for solving this problem. This implementation optimizes either time or space complexity compared to the previous approaches.

⏱ Time O(n) Optimized complexity 💾 Space O(1) Efficient space usage
function approach3():
// Implementation approach goes here
construct_binary_tree_from_preorder_and_inorder_traversal_space_optimized.py
"""
Solution for Construct Binary Tree from Preorder and Inorder Traversal
"""
def solve():
"""Implementation for approach 3 of Construct Binary Tree from Preorder and Inorder Traversal"""
pass
if __name__ == "__main__":
print("Solution ready")
  1. Preorder structure: preorder[0] is always the root of the current subtree. This is the entry point for every recursive call.

  2. Inorder partition: Once you find the root in inorder, the left portion is the left subtree and the right portion is the right subtree. This gives you exact boundaries for recursion.

  3. Index mapping: Using a hash map to store inorder indices is the performance key. It transforms an O(n) search into O(1) lookup, keeping overall time at O(n).

  4. Size calculation: The number of nodes in the left subtree is root_position_in_inorder - inorder_start. This lets you correctly split preorder without needing to create new arrays.