Construct a Binary Tree from Postorder and Inorder in Python

Building a binary tree from inorder and postorder traversals is a classic tree reconstruction problem. Given these two traversal sequences, we can uniquely reconstruct the original binary tree by leveraging the properties of each traversal type.

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

Algorithm Approach

The key insight is that in postorder traversal, the last element is always the root of the tree. We can use this root to split the inorder sequence into left and right subtrees.

Implementation

class TreeNode:
    def __init__(self, data, left=None, right=None):
        self.data = data
        self.left = left
        self.right = right

def print_inorder(root):
    """Print inorder traversal of the tree"""
    if root is not None:
        print_inorder(root.left)
        print(root.data, end=', ')
        print_inorder(root.right)

class Solution:
    def buildTree(self, inorder, postorder):
        """
        Build binary tree from inorder and postorder traversals
        """
        if inorder:
            # Last element in postorder is the root
            root = TreeNode(postorder.pop())
            
            # Find root's position in inorder
            root_index = inorder.index(root.data)
            
            # Build right subtree first (postorder processes right before left)
            root.right = self.buildTree(inorder[root_index + 1:], postorder)
            
            # Build left subtree
            root.left = self.buildTree(inorder[:root_index], postorder)
            
            return root

# Example usage
solution = Solution()
inorder_seq = [9, 3, 15, 20, 7]
postorder_seq = [9, 15, 7, 20, 3]

tree = solution.buildTree(inorder_seq, postorder_seq)

print("Reconstructed tree (inorder):")
print_inorder(tree)
Reconstructed tree (inorder):
9, 3, 15, 20, 7, 

How It Works

The algorithm works by recursively identifying roots and splitting sequences:

  1. Root Identification: The last element in postorder is always the current subtree's root
  2. Sequence Splitting: Find the root in inorder to determine left and right subtree elements
  3. Recursive Construction: Build right subtree first, then left (due to postorder's right-to-left processing)

Step-by-Step Execution

class TreeNodeWithSteps:
    def __init__(self, data):
        self.data = data
        self.left = None
        self.right = None

def build_tree_with_steps(inorder, postorder, level=0):
    """Build tree with step-by-step explanation"""
    indent = "  " * level
    
    if not inorder:
        print(f"{indent}Empty sequence - returning None")
        return None
    
    # Get root from postorder
    root_val = postorder.pop()
    root = TreeNodeWithSteps(root_val)
    print(f"{indent}Root: {root_val}")
    
    # Find root position in inorder
    root_index = inorder.index(root_val)
    print(f"{indent}Left subtree: {inorder[:root_index]}")
    print(f"{indent}Right subtree: {inorder[root_index + 1:]}")
    
    # Build subtrees
    root.right = build_tree_with_steps(inorder[root_index + 1:], postorder, level + 1)
    root.left = build_tree_with_steps(inorder[:root_index], postorder, level + 1)
    
    return root

# Demonstrate step-by-step construction
print("Step-by-step tree construction:")
inorder_demo = [9, 3, 15, 20, 7]
postorder_demo = [9, 15, 7, 20, 3]
result = build_tree_with_steps(inorder_demo, postorder_demo)
Step-by-step tree construction:
Root: 3
Left subtree: [9]
Right subtree: [15, 20, 7]
  Root: 20
  Left subtree: [15]
  Right subtree: [7]
    Root: 7
    Left subtree: []
    Right subtree: []
    Empty sequence - returning None
    Empty sequence - returning None
    Root: 15
    Left subtree: []
    Right subtree: []
    Empty sequence - returning None
    Empty sequence - returning None
  Root: 9
  Left subtree: []
  Right subtree: []
  Empty sequence - returning None
  Empty sequence - returning None

Time and Space Complexity

Aspect Complexity Explanation
Time O(n²) Finding index in inorder for each node
Space O(n) Recursion stack and tree storage

Optimized Version

class OptimizedSolution:
    def buildTree(self, inorder, postorder):
        """
        Optimized version using hashmap for O(1) index lookup
        """
        # Create hashmap for O(1) index lookup
        inorder_map = {val: i for i, val in enumerate(inorder)}
        
        def build(in_start, in_end):
            if in_start > in_end:
                return None
            
            # Get root from postorder
            root_val = postorder.pop()
            root = TreeNode(root_val)
            
            # Find root position using hashmap
            root_index = inorder_map[root_val]
            
            # Build right first, then left
            root.right = build(root_index + 1, in_end)
            root.left = build(in_start, root_index - 1)
            
            return root
        
        return build(0, len(inorder) - 1)

# Test optimized version
optimized_solution = OptimizedSolution()
inorder_test = [9, 3, 15, 20, 7]
postorder_test = [9, 15, 7, 20, 3]

optimized_tree = optimized_solution.buildTree(inorder_test, postorder_test)
print("Optimized solution result (inorder):")
print_inorder(optimized_tree)
Optimized solution result (inorder):
9, 3, 15, 20, 7, 

Conclusion

Constructing a binary tree from inorder and postorder traversals involves using the last element of postorder as the root and recursively building subtrees. The optimized version using a hashmap reduces time complexity from O(n²) to O(n) by eliminating linear index searches.

Updated on: 2026-03-25T10:07:35+05:30

732 Views

Kickstart Your Career

Get certified by completing the course

Get Started
Advertisements