Construct Binary Tree from Preorder and Inorder Traversal solution and explanation
9 more videos on this problem
Construct Binary Tree from Inorder and Preorder Traversal - Leetcode 105 - PythonNeetCode · 17:02 · 415K views
LeetCode 105. Construct Binary Tree from Preorder and Inorder Traversal (Algorithm Explained)Nick White · 11:45 · 85K views
Create Binary Tree from pre-order and in-order traversal (LeetCode 105) | Easiest explanationNikhil Lohia · 13:52 · 33K views
Leetcode - Construct Binary Tree from Preorder and Inorder Traversal (Python)Timothy H Chang · 8:43 · 3K views
LEETCODE 105 (JAVASCRIPT) | CONSTRUCT BINARY TREE FROM PREORDER AND INORDER TRAVERSALAndy Gala · 6:07 · 3K views
LeetCode 105 Construct Binary Tree from Preorder and Inorder Traversal | Blind 75Code In Motion · 16:12 · 2K views
Construct Binary Tree From Preorder And Inorder Traversal - LeetCode 105AlgoJS · 6:16 · 2K views
Construct Binary Tree from Preorder and Inorder Traversal | LeetCode 105 | Theory + PythonSai Anish Malla · 19:54 · 1K views
Daily Leetcode #835: Jul 14, 2022 - 105. Construct Binary Tree from Preorder and Inorder TraversalProgramming Live with Larry · 7:18 · 503 views
L34. Construct a Binary Tree from Preorder and Inorder Traversal | C++ | Javatake U forward · 18:52 · 455K views
The 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]
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.
Approach overview
Problem Overview: You receive two arrays representing the preorder and inorder traversal of a binary tree. Your task is to reconstruct the original binary tree structure. Each value appears exactly once, which guarantees a unique tree.
Approach 1: Recursive Divide and Conquer with Hash Map (O(n) time, O(n) space)
The key observation: preorder traversal always gives you the root first, while inorder traversal tells you how the tree splits into left and right subtrees. Start with the first element in preorder as the root. Use a hash map to store each value's index in the inorder array so you can locate the root position in O(1) time. Everything to the left of that index belongs to the left subtree, and everything to the right belongs to the right subtree.
Recursively repeat the same process for each subtree. Maintain a pointer that advances through the preorder array as you build nodes. This technique works because preorder determines node creation order, while inorder determines subtree boundaries. Building the index map reduces repeated searches and keeps the total runtime at O(n) with O(n) extra space for recursion and the hash map.
This solution combines divide and conquer with a hash table to efficiently rebuild the structure of a binary tree. Most interview solutions use this exact pattern.
Approach 2: Iterative Stack Simulation (O(n) time, O(n) space)
An iterative version simulates the recursive construction using a stack. Start by creating the root from the first preorder value and push it onto the stack. Traverse the preorder array and keep attaching nodes as left children until the current node matches the inorder pointer. Once a match occurs, pop nodes from the stack while values align with inorder, then attach the next node as a right child.
The stack represents the path of ancestors whose right subtree has not yet been constructed. The inorder pointer acts as a signal telling you when a subtree is finished. Each node is pushed and popped at most once, so the algorithm runs in O(n) time and uses O(n) stack space.
Recommended for interviews: The recursive hash map approach is the standard solution interviewers expect. It clearly demonstrates understanding of preorder vs inorder properties and divide-and-conquer reasoning. The iterative stack approach is also strong because it avoids recursion and shows deeper mastery of traversal mechanics.
Approach 1: Recursive Approach
This approach uses recursion to build the tree. We know that the first element of the preorder traversal is the root of the tree. We find that element in the inorder traversal to determine the elements of the left and right subtrees. We then recursively build the left and right subtrees.
Steps:
- Start with the entire range of the inorder traversal.
- The first element of the preorder list is the root; locate it in the inorder list.
- Divide the inorder list into left and right subtrees based on this root.
- Recursively build the left and right subtrees using the remaining elements of the preorder list.
- Use an index map to quickly locate the root in the inorder list, improving the performance to O(n).
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
class Solution:
def buildTree(self, preorder, inorder):
def helper(preorder_start, preorder_end, inorder_start, inorder_end):
if preorder_start > preorder_end:
return None
root_val = preorder[preorder_start]
root = TreeNode(root_val)
inorder_root_index = inorder_index_map[root_val]
left_tree_size = inorder_root_index - inorder_start
root.left = helper(preorder_start + 1, preorder_start + left_tree_size, inorder_start, inorder_root_index - 1)
root.right = helper(preorder_start + left_tree_size + 1, preorder_end, inorder_root_index + 1, inorder_end)
return root
inorder_index_map = {value: idx for idx, value in enumerate(inorder)}
n = len(preorder)
return helper(0, n - 1, 0, n - 1)import java.util.HashMap;
import java.util.Map;
public class Solution {
public static class TreeNode {
int val;
TreeNode left;
TreeNode right;
TreeNode(int x) { val = x; }
}
private Map<Integer, Integer> inorderIndexMap;
private int preorderIndex;
public TreeNode buildTree(int[] preorder, int[] inorder) {
inorderIndexMap = new HashMap<>();
for (int i = 0; i < inorder.length; i++) {
inorderIndexMap.put(inorder[i], i);
}
preorderIndex = 0;
return buildTree(preorder, 0, inorder.length - 1);
}
private TreeNode buildTree(int[] preorder, int inorderStart, int inorderEnd) {
if (inorderStart > inorderEnd) {
return null;
}
int rootVal = preorder[preorderIndex++];
TreeNode root = new TreeNode(rootVal);
int inorderRootIndex = inorderIndexMap.get(rootVal);
root.left = buildTree(preorder, inorderStart, inorderRootIndex - 1);
root.right = buildTree(preorder, inorderRootIndex + 1, inorderEnd);
return root;
}
}This Python solution defines a TreeNode class and constructs the tree using a helper function. The helper function takes indices to track the ranges in the preorder and inorder lists, creating new TreeNode objects as necessary and using an index map for efficient lookups. This approach recursively builds the tree, taking care of each node's left and right subtrees.
Time Complexity: O(n), where n is the number of nodes in the tree, because each node is processed once.
Space Complexity: O(n) for the recursion call stack and additional structures.
Approach 2: Iterative Approach
This approach uses an iterative method to build the binary tree using stacks. By leveraging the properties of the preorder and inorder sequences, we can keep track of the tree nodes that are yet to be processed. We utilize a stack data structure to maintain the hierarchy of nodes as they are constructed.
Steps:
- Initialize a stack with the root node derived from the first element of the preorder list.
- Iterate over the preorder traversal and use the stack to construct tree nodes.
- Maintain a pointer to track the current position in the inorder sequence, pushing new nodes onto the stack for left children and popping from the stack for right children.
#include <unordered_map>
#include <vector>
#include <stack>
using namespace std;
struct TreeNode {
int val;
TreeNode* left;
TreeNode* right;
TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};
class Solution {
public:
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
if (preorder.empty() || inorder.empty()) return nullptr;
unordered_map<int, int> inorderIndexMap;
for (int i = 0; i < inorder.size(); ++i) {
inorderIndexMap[inorder[i]] = i;
}
stack<TreeNode*> st;
TreeNode* root = new TreeNode(preorder[0]);
st.push(root);
int inorderIndex = 0;
for (int i = 1; i < preorder.size(); ++i) {
TreeNode* node = st.top();
if (inorderIndexMap[node->val] != inorderIndex) {
node->left = new TreeNode(preorder[i]);
st.push(node->left);
} else {
while (!st.empty() && inorderIndexMap[st.top()->val] == inorderIndex) {
node = st.top();
st.pop();
++inorderIndex;
}
node->right = new TreeNode(preorder[i]);
st.push(node->right);
}
}
return root;
}
};function TreeNode(val, left = null, right = null) {
this.val = val;
this.left = left;
this.right = right;
}
var buildTree = function(preorder, inorder) {
if (!preorder.length || !inorder.length) return null;
const inorderIndexMap = {};
inorder.forEach((val, idx) => {
inorderIndexMap[val] = idx;
});
const stack = [];
const root = new TreeNode(preorder[0]);
stack.push(root);
let inorderIndex = 0;
for (let i = 1; i < preorder.length; i++) {
let node = stack[stack.length - 1];
if (inorderIndexMap[node.val] !== inorderIndex) {
node.left = new TreeNode(preorder[i]);
stack.push(node.left);
} else {
while (stack.length && inorderIndexMap[stack[stack.length - 1].val] === inorderIndex) {
node = stack.pop();
inorderIndex++;
}
node.right = new TreeNode(preorder[i]);
stack.push(node.right);
}
}
return root;
};This C++ solution employs an iterative approach using a stack. By iterating through the preorder list, it constructs the tree and manages left and right children using the stack. This method avoids recursion and utilizes an index map for inorder positions.
Time Complexity: O(n), where n is the number of nodes, as each node is visited once.
Space Complexity: O(n) for the stack and additional data structures like the map for indices.
Approach 3: Hash Table + Recursion
The first node preorder[0] in the pre-order sequence is the root node. We find the position k of the root node in the in-order sequence, which can divide the in-order sequence into the left subtree inorder[0..k] and the right subtree inorder[k+1..].
Through the intervals of the left and right subtrees, we can calculate the number of nodes in the left and right subtrees, assumed to be a and b. Then in the pre-order nodes, the a nodes after the root node are the left subtree, and the b nodes after that are the right subtree.
Therefore, we design a function dfs(i, j, n), where i and j represent the starting positions of the pre-order sequence and the in-order sequence, respectively, and n represents the number of nodes. The return value of the function is the binary tree constructed with preorder[i..i+n-1] as the pre-order sequence and inorder[j..j+n-1] as the in-order sequence.
The execution process of the function dfs(i, j, n) is as follows:
- If
n leq 0, it means there are no nodes, return a null node. - Take out the first node
v = preorder[i]of the pre-order sequence as the root node, and then use the hash tabledto find the positionkof the root node in the in-order sequence. Then the number of nodes in the left subtree isk - j, and the number of nodes in the right subtree isn - k + j - 1. - Recursively construct the left subtree
l = dfs(i + 1, j, k - j)and the right subtreer = dfs(i + 1 + k - j, k + 1, n - k + j - 1). - Finally, return the binary tree with
vas the root node andlandras the left and right subtrees, respectively.
The time complexity is O(n), and the space complexity is O(n). Here, n is the number of nodes in the binary tree.
/**
* Definition for a binary tree node.
* class TreeNode {
* val: number
* left: TreeNode | null
* right: TreeNode | null
* constructor(val?: number, left?: TreeNode | null, right?: TreeNode | null) {
* this.val = (val===undefined ? 0 : val)
* this.left = (left===undefined ? null : left)
* this.right = (right===undefined ? null : right)
* }
* }
*/
function buildTree(preorder: number[], inorder: number[]): TreeNode | null {
const d: Map<number, number> = new Map();
const n = inorder.length;
for (let i = 0; i < n; ++i) {
d.set(inorder[i], i);
}
const dfs = (i: number, j: number, n: number): TreeNode | null => {
if (n <= 0) {
return null;
}
const v = preorder[i];
const k = d.get(v)!;
const l = dfs(i + 1, j, k - j);
const r = dfs(i + 1 + k - j, k + 1, n - 1 - (k - j));
return new TreeNode(v, l, r);
};
return dfs(0, 0, n);
}/**
* Definition for a binary tree node.
* type TreeNode struct {
* Val int
* Left *TreeNode
* Right *TreeNode
* }
*/
func buildTree(preorder []int, inorder []int) *TreeNode {
d := map[int]int{}
for i, x := range inorder {
d[x] = i
}
var dfs func(i, j, n int) *TreeNode
dfs = func(i, j, n int) *TreeNode {
if n <= 0 {
return nil
}
v := preorder[i]
k := d[v]
l := dfs(i+1, j, k-j)
r := dfs(i+1+k-j, k+1, n-1-(k-j))
return &TreeNode{v, l, r}
}
return dfs(0, 0, len(preorder))
}// Definition for a binary tree node.
// #[derive(Debug, PartialEq, Eq)]
// pub struct TreeNode {
// pub val: i32,
// pub left: Option<Rc<RefCell<TreeNode>>>,
// pub right: Option<Rc<RefCell<TreeNode>>>,
// }
//
// impl TreeNode {
// #[inline]
// pub fn new(val: i32) -> Self {
// TreeNode {
// val,
// left: None,
// right: None
// }
// }
// }
use std::cell::RefCell;
use std::collections::HashMap;
use std::rc::Rc;
impl Solution {
pub fn build_tree(preorder: Vec<i32>, inorder: Vec<i32>) -> Option<Rc<RefCell<TreeNode>>> {
let mut d = HashMap::new();
for (i, &x) in inorder.iter().enumerate() {
d.insert(x, i);
}
Self::dfs(&preorder, &d, 0, 0, preorder.len())
}
pub fn dfs(
preorder: &Vec<i32>,
d: &HashMap<i32, usize>,
i: usize,
j: usize,
n: usize,
) -> Option<Rc<RefCell<TreeNode>>> {
if n <= 0 {
return None;
}
let v = preorder[i];
let k = d[&v];
let mut root = TreeNode::new(v);
root.left = Self::dfs(preorder, d, i + 1, j, k - j);
root.right = Self::dfs(preorder, d, i + k - j + 1, k + 1, n - k + j - 1);
Some(Rc::new(RefCell::new(root)))
}
}Complexity comparison
| Approach | Complexity |
|---|---|
| Recursive Approach | Time Complexity: O(n), where n is the number of nodes in the tree, because each node is processed once. |
| Iterative Approach | Time Complexity: O(n), where n is the number of nodes, as each node is visited once. |
| Hash Table + Recursion | – |
Complexity analysis
| Approach | Time | Space | When to Use |
|---|---|---|---|
| Recursive Divide and Conquer with Hash Map | O(n) | O(n) | Most common interview solution. Clear logic using preorder for roots and inorder for subtree boundaries. |
| Iterative Stack Construction | O(n) | O(n) | Useful when avoiding recursion or demonstrating deeper understanding of traversal mechanics. |
Frequently asked questions
Is Construct Binary Tree from Preorder and Inorder Traversal easy or hard?
Construct Binary Tree from Preorder and Inorder Traversal is generally classified as a medium difficulty problem. The challenge lies in understanding how preorder identifies the root while inorder determines subtree partitions. Once that relationship is clear, the divide-and-conquer implementation becomes straightforward.
Construct Binary Tree from Preorder and Inorder Traversal Python/Java solution
Python and Java implementations typically follow the recursive approach. Build a hash map from inorder values to indices, then recursively construct nodes using the preorder pointer and inorder boundaries. Both implementations run in O(n) time and O(n) space.
How to solve Construct Binary Tree from Preorder and Inorder Traversal in O(n)?
Store the index of every value from the inorder array in a hash map. Use a pointer that moves through the preorder array to create nodes in root-first order. For each node, split the inorder range into left and right parts and recursively build the subtrees. Because each node and lookup occurs once, the algorithm runs in O(n) time.
What is the best approach for Construct Binary Tree from Preorder and Inorder Traversal?
The best approach uses recursive divide and conquer with a hash map for quick index lookup in the inorder array. Preorder identifies the root node, while inorder determines the left and right subtree boundaries. Using a hash map avoids repeated searches and keeps the total time complexity at O(n). This is the standard interview solution.
Is Construct Binary Tree from Preorder and Inorder Traversal asked at Google/Amazon/Meta?
Construct Binary Tree from Preorder and Inorder Traversal is a common medium-level tree problem asked in interviews at companies like Amazon, Google, and Meta. It tests understanding of tree traversals, recursion, and divide-and-conquer techniques. Variations of this reconstruction problem appear frequently in technical interviews.
What data structure is used in Construct Binary Tree from Preorder and Inorder Traversal?
The solution uses a binary tree structure along with a hash map to store inorder indices for quick lookup. Recursion or a stack manages the construction process. These structures allow efficient subtree splitting and ensure the algorithm runs in linear time.
What is the time complexity of Construct Binary Tree from Preorder and Inorder Traversal?
The optimal solution runs in O(n) time because each node is processed exactly once. A hash map allows O(1) lookup of each value's index in the inorder traversal. Space complexity is O(n) due to the recursion stack and the hash map storing inorder indices.
