65. Construct Binary Tree from Preorder and Inorder Traversal
Problem Statement
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]
Solution
# 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
class Solution:
def buildTree(self, preorder: list[int], inorder: list[int]) -> TreeNode:
# Create a hash map for quick lookup of values in inorder traversal
inorder_map = {val: idx for idx, val in enumerate(inorder)}
def build(preorder_start, preorder_end, inorder_start, inorder_end):
# Base case: if the range is invalid, return None
if preorder_start > preorder_end or inorder_start > inorder_end:
return None
# The first element in preorder is always the root of the current subtree
root_val = preorder[preorder_start]
root = TreeNode(root_val)
# Find the root_val in inorder traversal to determine left and right subtree boundaries
inorder_root_idx = inorder_map[root_val]
# Calculate the size of the left subtree
left_subtree_size = inorder_root_idx - inorder_start
# Recursively build the left subtree
root.left = build(
preorder_start + 1, # Next element in preorder is left child
preorder_start + left_subtree_size, # End of left subtree in preorder
inorder_start, # Start of left subtree in inorder
inorder_root_idx - 1 # End of left subtree in inorder
)
# Recursively build the right subtree
root.right = build(
preorder_start + left_subtree_size + 1, # Start of right subtree in preorder
preorder_end, # End of right subtree in preorder
inorder_root_idx + 1, # Start of right subtree in inorder
inorder_end # End of right subtree in inorder
)
return root
# Initial call to build the tree
return build(0, len(preorder) - 1, 0, len(inorder) - 1)
Explanation
This is a classic tree construction problem that can be solved recursively. The key properties of preorder and inorder traversals are crucial here:
- Preorder Traversal:
[Root, Left Subtree, Right Subtree]
- Inorder Traversal:
[Left Subtree, Root, Right Subtree]
Core Idea:
- The first element in the
preorder
traversal is always the root of the current subtree. - Once we identify the root from
preorder
, we can find its position in theinorder
traversal. This position divides theinorder
array into two parts: the left part represents the left subtree, and the right part represents the right subtree. - The sizes of these left and right parts in
inorder
tell us the sizes of the left and right subtrees. We can then use these sizes to determine the corresponding ranges in thepreorder
array for the left and right subtrees.
Recursive build
Function:
-
The
build
function takes the start and end indices for bothpreorder
andinorder
arrays, defining the current segment of the tree we are trying to build. -
Base Case: If
preorder_start > preorder_end
orinorder_start > inorder_end
, it means we have an empty subtree, so returnNone
. -
Create Root: The
root_val
ispreorder[preorder_start]
. Create aTreeNode
with this value. -
Find Root in Inorder: Use a hash map (
inorder_map
) to quickly find the index ofroot_val
in theinorder
array. Thisinorder_root_idx
is critical. -
Calculate Subtree Sizes: The number of elements in the left subtree is
left_subtree_size = inorder_root_idx - inorder_start
. -
Recursive Calls:
- Left Subtree: Recursively call
build
for the left subtree. Thepreorder
range will be[preorder_start + 1, preorder_start + left_subtree_size]
. Theinorder
range will be[inorder_start, inorder_root_idx - 1]
. - Right Subtree: Recursively call
build
for the right subtree. Thepreorder
range will be[preorder_start + left_subtree_size + 1, preorder_end]
. Theinorder
range will be[inorder_root_idx + 1, inorder_end]
.
- Left Subtree: Recursively call
-
Return Root: Return the constructed
root
node.
Optimization: Using a hash map for inorder_map
allows for O(1) lookup of the root's index, which is crucial for performance.
Time and Space Complexity:
- Time Complexity: O(N), where N is the number of nodes in the tree. Each node is processed once, and the hash map lookup is O(1).
- Space Complexity: O(N) for the
inorder_map
and O(H) for the recursion stack (where H is the height of the tree, which can be N in the worst case).