Construct Binary Tree From Preorder And Inorder Traversal
Problem
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]
Brainstorming
Brute force
okay so what is pre order root->left->right inorder left->root-> right
we need to make a tree node by node using these 2 informations
# Definition for a binary tree node.
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
on the face of it a crude way to think of it would be to see the array indexes as position like for eg
in array preorder the value at preorder[0] will always be the root of the binary tree so we can just do
root = TreeNode(preorder[0])
at each node we would then need to decide what goes to its left and what goes to its right
like in the above case
root.left = TreeNode(preorder[1])
root.right = TreeNode(preorder[2])
but it would be very tricky to pick array indexes in insolation and force fit them in the tree especially in a loop we have another array in order maybe we can use that
lets take the above example and try to see if we can come up with a pattern
0 1. 2. 3. 4
preorder = [3, 9, 20, 15, 7]
r r.l r.r r.r.l r.r.r
inorder = [9, 3, 15, 20, 7]
r.l. r. r.r.l r.r. r.r.r
with array indexes it will be pretty hard to keep a track of maybe some sort of LIFO or FIFO data structure can help that give us a way to peel off this but even then it will be slightly challlenging. A heap is a complete binary tree maybe we can check that. but i am concerned that with its property of complete tree and keeping the minimum and maximum can cause a problem , since we are dealing with a binary tree and not a binary search tree
since we have 2 arrays maybe we can come up with an algorithm that takes the heaps priority queue property along with it?
- 🍅 (pomodoro::WORK) (duration:: 20m) (begin:: 2025-12-16 10:42) - (end:: 2025-12-16 11:02)
Optimal Solution
O(N^2) Brute force
This is a brilliant brainstorming session. You’ve correctly identified the most important piece of information, asked the right questions, and even considered (and correctly reasoned about) alternative data structures.
Your Core Insight (The Key to the Entire Problem):
“in array preorder the value at
preorder[0]will always be the root of the binary tree”
100% correct. This is the starting point for everything. The first element of any preorder traversal is the root of the tree (or subtree) that traversal represents.
Your Next Insight (Also Correct):
“we have another array in order maybe we can use that”
Yes. Once you know the root’s value (from preorder[0]), the inorder array tells you everything you need to know about the structure.
- Find the root’s value in the
inorderarray. - Everything to the left of the root’s value in the
inorderarray belongs to the left subtree. - Everything to the right of the root’s value in the
inorderarray belongs to the right subtree.
Let’s use your example:
preorder = [3, 9, 20, 15, 7]
inorder = [9, 3, 15, 20, 7]
- Root: From
preorder, the root is3. - Find Root in
inorder:3is at index 1 in theinorderarray. - Partition
inorder:- Left subtree elements (from
inorder):[9] - Right subtree elements (from
inorder):[15, 20, 7]
- Left subtree elements (from
This is the recursive “Aha!” moment.
We now have two smaller, independent subproblems:
- Subproblem 1 (Left Child): Construct a tree from its
inordertraversal[9]and itspreordertraversal. - Subproblem 2 (Right Child): Construct a tree from its
inordertraversal[15, 20, 7]and itspreordertraversal.
This immediately tells us that recursion is the natural way to solve this.
The Self-Nudge Questions:
Question 1: Finding the Sub-Array Traversals
We have the inorder arrays for the left and right subtrees. But what are their preorder arrays?
- The left subtree’s
inorderis[9]. It has 1 element. - The right subtree’s
inorderis[15, 20, 7]. It has 3 elements.
Now look at the original preorder: [3, 9, 20, 15, 7]
- The root is
3. - The next elements in a preorder traversal are always from the left subtree. How many elements are in the left subtree? We just found out: 1 element.
- So, the preorder traversal for the left subtree must be the next 1 element after the root:
[9] - The elements after that must be the preorder traversal for the right subtree.
- So, the preorder traversal for the right subtree must be
[20, 15, 7].
Question 2: Structuring the Recursion
So, our recursive function will build a tree for a given preorder and inorder subarray.
build(preorder_subarray, inorder_subarray)
Inside this function:
- Base Case: If the input arrays are empty, what should you return?
- Find the root: Take the first element of the
preorder_subarray. - Find the partition point: Find that root’s value in the
inorder_subarray. - Create the subproblems: Slice the
inorder_subarrayandpreorder_subarrayto get the correct arrays for the left and right children. - Recurse:
root.left = build(left_preorder_subarray, left_inorder_subarray)root.right = build(right_preorder_subarray, right_inorder_subarray)
- Return the
rootyou created.
This approach is correct, but slicing arrays (array[:]) over and over is inefficient (O(N)).
Question 3 (The Optimisation):
Instead of passing copies of subarrays, can we just pass pointers/indices that define the boundaries of the current preorder and inorder subarrays we’re working on?
build(preorder, inorder, pre_start, pre_end, in_start, in_end)
(Prompt): Focus on the inefficient slicing version first (Question 2). Try to write the pseudocode for build(preorder_subarray, inorder_subarray). How do you calculate the slices for the recursive calls? (Hint: you need the len of the left inorder subarray).
okay so got it , the heap approach maybe was me going a bit astray i think my original insights combined with recursive approach would work the best here so if i understand correctly the brute force approach would be array slicing to the multitude
the flow is something like this
- for every tree we find the root using the preorder traversal at the start the root is at index 0 going ahead it would be somewhere ahead in the array for subtrees
- we find the index of this root in the inorder traversal lets call it i. once we find it the elements from 0 till that index
iwill be the left subtreein_order_left_tree_at_root_i = in_order[:i]anything from i+1 till the end of the array will be right_subtreein_order_right_tree_at_root_i = in_order[i+1:] - We need to do this similar exercise for post order too . This can be done using the length of the in_order_arrays
- we have
in_order_left_tree_at_root_iwe know in preorder the number of elements in this array is equal to the number of elements in pre order after the root that are the left of the root , and post that what we have is the elements to the right of the rootpre_order_left_tree_at_root_i = pre_order[1:len(in_order_left_tree_at_root_i+1]and the right of it will bepre_order_right_tree_at_root_i = [len(in_order_left_tree_at_root_i+1:]
- we have
- to make this brute force work we will cheat to get the index for the root in inorder array using
i = inorder_array.index(root)
below is the python code
This is the Brute force solution
class TreeNode:
def __init__(self, val=None, left=None, right=None):
self.val = val
self.left = left
self.right = right
pre_order = [3,9,20,15,7]
in_order = [9,3,15,20,7]
def build(pre_order_sub_array, in_order_sub_array):
# base case
if not pre_order_sub_array or not in_order_sub_array
return None
root = TreeNode(pre_order_sub_array[0])
# index of root
i = in_order_sub_array.index(pre_order_sub_array[0])
# get the inorder sub array
in_order_left_tree_at_root_i = in_order_sub_array[:i]
in_order_right_tree_at_root_i = in_order_sub_array[i+1:]
# get the pre order sub array
pre_order_left_tree_at_root_i = pre_order_sub_array[1:len(in_order_left_tree_at_root_i)+1]
pre_order_right_tree_at_root_i = pre_order_sub_array[len(in_order_left_tree_at_root_i)+1:]
root.left = build(pre_order_left_tree_at_root_i, in_order_left_tree_at_root_i)
root.right = build(pre_order_right_tree_at_root_i, in_order_right_tree_at_root_i)
return root
build(pre_order, in_order)
Optimisation
- 🍅 (pomodoro::WORK) (duration:: 30m) (begin:: 2025-12-16 11:02) - (end:: 2025-12-16 12:00)
The Inefficiency:
The two main inefficiencies are:
- in_order_sub_array.index(…): This is an O(N) scan inside each recursive call.
- pre_order_sub_array
[:]: Slicing creates copies of the arrays, which takes time and memory.
Instead of passing copies of subarrays, can we just pass pointers/indices that define the boundaries of the current preorder and inorder subarrays we’re working on?
This is the key. Let’s design a helper function that works with indices.
build_helper(pre_start, pre_end, in_start, in_end)
- pre_start, pre_end: The boundaries for the current preorder subarray.
- in_start, in_end: The boundaries for the current inorder subarray.
To speed up the .index() call, what if we pre-process the inorder array just once at the very beginning? We could build a hash map from value -> index. This would make finding the root’s index an O(1) lookup.
Let’s structure the optimised approach:
Main Function build(preorder, inorder):
- Create a
value_to_inorder_indexmap from theinorderarray. - Call the recursive helper with the initial full boundaries:
build_helper(0, len(preorder)-1, 0, len(inorder)-1).
- Create a
Recursive Helper build_helper(pre_start, pre_end, in_start, in_end):
- Base Case: If pre_start > pre_end or in_start > in_end, the subarray is empty. Return None.
- Find Root: root_val =
preorder[pre_start]. Create the root = TreeNode(root_val). - Find Partition Point:
root_inorder_idx=value_to_inorder_index[root_val]. This is now O(1). - Calculate Left Subtree Size:
left_subtree_size=root_inorder_idx- in_start. This is the crucial calculation. - Recurse Left:
- The left
inorderpart is from in_start toroot_inorder_idx- 1. - The left preorder part starts right after the root (pre_start + 1) and goes for left_subtree_size elements.
- root.left = build_helper(…)
- The left
- Recurse Right:
- The right
inorderpart is fromroot_inorder_idx+ 1 to in_end. - The right preorder part starts right after the left preorder part.
- root.right = build_helper(…)
- The right
- Return root.
got it so here is my attempt to solve this problem
class TreeNode:
def __init__(self, val=None, left=None, right=None):
self.val = val
self.left = left
self.right = right
pre_order = [3,9,20,15,7]
in_order = [9,3,15,20,7]
def construct_bt_pre_in_order(pre_order, in_order):
# create a hashmap for O(1) lookup of root indexes in inorder array
index_lookup_in_inorder = {}
for index,val in enumerate(in_order):
index_lookup_in_inorder.update({val:index})
def build_helper(pre_start, pre_end, in_start, in_end):
# Base case if the array is empty
if pre_start > pre_end or in_start > in_end:
return None
root = TreeNode(pre_order[start])
root_index_in_in_order = index_lookup_in_inorder[root]
left_subtree_size = root_index_in_in_order - in_start
left_in_order_end = root_index_in_in_order -1
left_pre_order_start = pre_start +1
right_in_order_start = root_index_in_in_order + 1
right_pre_order_start = left_pre_order+ left_subtree_size - 1
root.left = build_helper(left_pre_order_start,left_subtree_size,in_start, left_in_order_end)
root.right = build_helper( right_pre_order_start+1,pre_end ,right_in_order_start, in_end)
return root
binary_tree = build_helper(0,len(pre_order)-1, 0, len(in_order)-1)
return binary_tree
tree = construct_bt_pre_in_order(pre_order, in_order)
- 🥤 (pomodoro::BREAK) (duration:: 15m) (begin:: 2025-12-16 12:01) - (end:: 2025-12-16 12:16)
You are just getting tangled up in the final index calculations for the recursive calls, which is extremely common. Let’s break it down clearly.
State before recursive calls:
- pre_start: Start of preorder slice.
- in_start: Start of inorder slice.
- root_val =
preorder[pre_start] - root_inorder_idx: Index of root_val in the full inorder array.
- left_subtree_size = root_inorder_idx - in_start
Let’s Define the Boundaries for the LEFT Subtree:
Inorder: The inorder for the left subtree is easy. It starts at in_start and ends just before the root.
- in_start_left = in_start
- in_end_left = root_inorder_idx - 1
Preorder: The preorder for the left subtree starts right after the root in the preorder array.
- pre_start_left = pre_start + 1
- How long is it? It’s left_subtree_size elements long. So it ends at pre_start + left_subtree_size.
- pre_end_left = pre_start + left_subtree_size
Now, Let’s Define the Boundaries for the RIGHT Subtree:
Inorder: The inorder for the right subtree starts right after the root.
- in_start_right = root_inorder_idx + 1
- in_end_right = in_end
Preorder: The preorder for the right subtree starts right after the preorder for the left subtree ends.
- The left subtree’s preorder ended at pre_start + left_subtree_size.
- So, pre_start_right = pre_start + left_subtree_size + 1.
- It goes all the way to the end of the current preorder slice.
- pre_end_right = pre_end
Attempt 2
class TreeNode:
def __init__(self, val=None, left=None, right=None):
self.val = val
self.left = left
self.right = right
pre_order = [3,9,20,15,7]
in_order = [9,3,15,20,7]
def construct_bt_pre_in_order(pre_order, in_order):
# create a hashmap for O(1) lookup of root indexes in inorder array
index_lookup_in_inorder = {}
for index,val in enumerate(in_order):
index_lookup_in_inorder.update({val:index})
def build_helper(pre_start, pre_end, in_start, in_end):
# Base case if the array is empty
if pre_start > pre_end or in_start > in_end:
return None
root = TreeNode(pre_order[pre_start])
root_index_in_in_order = index_lookup_in_inorder[root.val]
left_subtree_size = root_index_in_in_order - in_start
# The inorder for the left subtree is easy. It starts at in_start and ends just before the root.
in_start_left = in_start
in_end_left = root_index_in_in_order -1
# Preorder: The preorder for the left subtree starts right after the root in the preorder array.
pre_start_left = pre_start +1
pre_end_left = pre_start + left_subtree_size
# **Inorder:** The inorder for the right subtree starts right after the root.
in_start_right = root_index_in_in_order + 1
in_end_right = in_end
# **Preorder:** The preorder for the right subtree starts right after the preorder for the left
pre_start_right = pre_start + left_subtree_size + 1
pre_end_right = pre_end
root.left = build_helper(pre_start_left, pre_end_left, in_start_left,in_end_left)
root.right = build_helper(pre_start_right,pre_end_right, in_start_right, in_end_right)
return root
binary_tree = build_helper(0,len(pre_order)-1, 0, len(in_order)-1)
return binary_tree
tree = construct_bt_pre_in_order(pre_order, in_order)
- 🍅 (pomodoro::WORK) (duration:: 30m) (begin:: 2025-12-16 12:16) - (end:: 2025-12-16 12:46)