Skip to content

149. Construct Binary Tree from Preorder and Inorder Traversal

šŸ”— LeetCode Problem: 105. Construct Binary Tree from Preorder and Inorder Traversal
šŸ“Š Difficulty: Medium
šŸ·ļø Topics: Binary Trees, Array, Divide and Conquer, Recursion, HashMap

Problem Statement

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

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 <= 3000 - inorder.length == preorder.length - -3000 <= preorder[i], inorder[i] <= 3000 - preorder and inorder consist of unique values - Each value of inorder also appears in preorder - preorder is guaranteed to be the preorder traversal of the tree - inorder is guaranteed to be the inorder traversal of the tree


🌳 Visual Understanding - How Traversals Encode Trees

Traversal Definitions Recap:

PREORDER (Root → Left → Right):
  Process root FIRST
  Then entire left subtree
  Then entire right subtree

INORDER (Left → Root → Right):
  Process left subtree FIRST
  Then root
  Then right subtree

The Key Insight - What Each Traversal Tells Us:

PREORDER tells us:
  - First element = ROOT
  - Next elements = left subtree (but mixed together)
  - Last elements = right subtree (but mixed together)

INORDER tells us:
  - Elements before root = left subtree
  - Root in middle
  - Elements after root = right subtree

Together they UNIQUELY determine the tree! šŸ”‘

Example 1 - Visual Breakdown:

Given:
  preorder = [3, 9, 20, 15, 7]
  inorder  = [9, 3, 15, 20, 7]

STEP 1: Find root from preorder
  preorder[0] = 3
  Root = 3 āœ“

STEP 2: Find root in inorder
  inorder = [9, | 3 |, 15, 20, 7]
            ↑    ↑     ↑
          left  root  right

STEP 3: Partition inorder
  Left subtree:  [9]
  Right subtree: [15, 20, 7]

STEP 4: Partition preorder accordingly
  Root: 3
  Left:  [9]      (next 1 element after root)
  Right: [20, 15, 7]  (remaining elements)

STEP 5: Current tree structure
       3
      / \
     ?   ?
    /     \
  [9]   [15,20,7]

Recursively build left and right!

Visual Tree Construction:

Step 1: Root = 3
    3

Step 2: Build left subtree from [9] and [9]
    Root = 9 (no children)

    3
   /
  9

Step 3: Build right subtree from [20,15,7] and [15,20,7]
    Root = 20
    Left = [15]
    Right = [7]

    3
   / \
  9   20
     /  \
    15   7

Final tree! āœ“

Another Example - Understanding Partitioning:

preorder = [1, 2, 4, 5, 3]
inorder  = [4, 2, 5, 1, 3]

STEP 1: Root = 1 (first in preorder)

STEP 2: Find 1 in inorder
  [4, 2, 5, | 1 |, 3]
   ↑         ↑    ↑
  left      root right

STEP 3: Left subtree has 3 elements: [4, 2, 5]
        Right subtree has 1 element: [3]

STEP 4: Split preorder:
  Skip root (1)
  Take next 3 for left: [2, 4, 5]
  Take remaining for right: [3]

STEP 5: Recursively build:
  Left:  preorder=[2,4,5], inorder=[4,2,5]
  Right: preorder=[3], inorder=[3]

Tree:
       1
      / \
     2   3
    / \
   4   5

🧠 The AHA Moment - The Algorithm

The Core Algorithm:

buildTree(preorder, inorder):
  1. Root = preorder[0]
  2. Find root index in inorder
  3. Elements before root in inorder = left subtree
  4. Elements after root in inorder = right subtree
  5. Partition preorder based on left subtree size
  6. Recursively build left and right subtrees
  7. Connect and return

Divide and conquer! šŸŽÆ

Visual Algorithm Flow:

preorder = [3, 9, 20, 15, 7]
inorder  = [9, 3, 15, 20, 7]

ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”
│ Find root: preorder[0] = 3         │
│ Find in inorder: index 1           │
│                                    │
│ Partition inorder:                 │
│   Left:  [9]     (indices 0-0)    │
│   Right: [15,20,7] (indices 2-4)  │
│                                    │
│ Partition preorder:                │
│   Left size = 1                    │
│   Left:  [9]     (indices 1-1)    │
│   Right: [20,15,7] (indices 2-4)  │
ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜
         ↓
    Recurse on left and right

🧠 Recursive Thinking - Building the Solution

Base Case:

When to stop?
  → When preorder or inorder is empty

What to return?
  → null (no tree)

if (preStart > preEnd) return null;
// OR
if (inStart > inEnd) return null;

Recursive Case:

For any valid range:

STEP 1: Create root from preorder
  int rootVal = preorder[preStart];
  TreeNode root = new TreeNode(rootVal);

STEP 2: Find root in inorder
  int rootIndex = findInInorder(rootVal);

STEP 3: Calculate left subtree size
  int leftSize = rootIndex - inStart;

STEP 4: Recursively build left subtree
  root.left = buildTree(
    preorder, preStart + 1, preStart + leftSize,
    inorder, inStart, rootIndex - 1
  );

STEP 5: Recursively build right subtree
  root.right = buildTree(
    preorder, preStart + leftSize + 1, preEnd,
    inorder, rootIndex + 1, inEnd
  );

STEP 6: Return constructed root
  return root;

What Each Call Returns:

Each recursive call returns:
  A TreeNode that is the root of a subtree

Parent connects this to:
  - root.left (if left subtree)
  - root.right (if right subtree)

Example:
  buildTree([9], [9]) returns:
    TreeNode(9)

  Parent uses this:
    root.left = TreeNode(9)

šŸŽÆ Solution: Divide and Conquer with HashMap

Implementation:

class Solution {
    private Map<Integer, Integer> inorderMap;

    public TreeNode buildTree(int[] preorder, int[] inorder) {
        // Build hashmap for O(1) lookup of root in inorder
        // WHY? We'll search for root many times
        inorderMap = new HashMap<>();
        for (int i = 0; i < inorder.length; i++) {
            inorderMap.put(inorder[i], i);
        }

        // Start recursive building
        return build(preorder, 0, preorder.length - 1,
                    inorder, 0, inorder.length - 1);
    }

    private TreeNode build(int[] preorder, int preStart, int preEnd,
                          int[] inorder, int inStart, int inEnd) {
        // Base case: no elements to construct tree
        if (preStart > preEnd || inStart > inEnd) {
            return null;
        }

        // STEP 1: First element in preorder is root
        // WHY? Preorder visits root first!
        int rootVal = preorder[preStart];
        TreeNode root = new TreeNode(rootVal);

        // STEP 2: Find root position in inorder
        // WHY? Inorder tells us left vs right split
        int rootIndex = inorderMap.get(rootVal);

        // STEP 3: Calculate left subtree size
        // WHY? Need to know how to split preorder array
        int leftSize = rootIndex - inStart;

        // STEP 4: Recursively build left subtree
        // Preorder: skip root, take next 'leftSize' elements
        // Inorder: elements before root
        root.left = build(preorder, preStart + 1, preStart + leftSize,
                         inorder, inStart, rootIndex - 1);

        // STEP 5: Recursively build right subtree
        // Preorder: elements after left subtree
        // Inorder: elements after root
        root.right = build(preorder, preStart + leftSize + 1, preEnd,
                          inorder, rootIndex + 1, inEnd);

        // STEP 6: Return constructed subtree
        return root;
    }
}

Why HashMap?

Without HashMap:
  For each node, search for root in inorder: O(n)
  Total: O(n) nodes Ɨ O(n) search = O(n²)

With HashMap:
  Precompute all positions: O(n) once
  Lookup for each node: O(1)
  Total: O(n) āœ“

Optimization from O(n²) to O(n)! šŸš€

šŸ” Detailed Dry Run - Complete Recursion

preorder = [3, 9, 20, 15, 7]
inorder  = [9, 3, 15, 20, 7]

inorderMap = {9:0, 3:1, 15:2, 20:3, 7:4}

Complete Call Stack Visualization:

═══════════════════════════════════════════════════
CALL STACK - BUILDING THE TREE
═══════════════════════════════════════════════════

Level 0: build(pre[0-4], in[0-4])
│
ā”œā”€ rootVal = preorder[0] = 3
ā”œā”€ Create node: TreeNode(3)
ā”œā”€ rootIndex = inorderMap.get(3) = 1
ā”œā”€ leftSize = 1 - 0 = 1
│
ā”œā”€ LEFT: build(pre[1-1], in[0-0]) ────────────────┐
│                                                  │
│  Level 1: build(pre[1-1], in[0-0])             │
│  │                                               │
│  ā”œā”€ preStart=1, preEnd=1                       │
│  ā”œā”€ inStart=0, inEnd=0                         │
│  │                                               │
│  ā”œā”€ rootVal = preorder[1] = 9                  │
│  ā”œā”€ Create node: TreeNode(9)                   │
│  ā”œā”€ rootIndex = inorderMap.get(9) = 0          │
│  ā”œā”€ leftSize = 0 - 0 = 0                       │
│  │                                               │
│  ā”œā”€ LEFT: build(pre[2-1], in[0--1])           │
│  │  → preStart > preEnd → return null          │
│  │                                               │
│  ā”œā”€ RIGHT: build(pre[2-1], in[1-0])           │
│  │  → preStart > preEnd → return null          │
│  │                                               │
│  └─ return TreeNode(9)                          │
│     └─ Node structure: 9                        │
│                        / \                       │
│                     null null                    │
│                                                  │
ā”œā”€ 3.left = TreeNode(9)                           │
│                                                  │
ā”œā”€ RIGHT: build(pre[2-4], in[2-4]) ───────────────┐
│                                                  │
│  Level 1: build(pre[2-4], in[2-4])             │
│  │                                               │
│  ā”œā”€ preStart=2, preEnd=4                       │
│  ā”œā”€ inStart=2, inEnd=4                         │
│  │                                               │
│  ā”œā”€ rootVal = preorder[2] = 20                 │
│  ā”œā”€ Create node: TreeNode(20)                  │
│  ā”œā”€ rootIndex = inorderMap.get(20) = 3         │
│  ā”œā”€ leftSize = 3 - 2 = 1                       │
│  │                                               │
│  ā”œā”€ LEFT: build(pre[3-3], in[2-2]) ───────┐   │
│  │                                         │   │
│  │  Level 2: build(pre[3-3], in[2-2])    │   │
│  │  │                                      │   │
│  │  ā”œā”€ rootVal = preorder[3] = 15        │   │
│  │  ā”œā”€ Create node: TreeNode(15)         │   │
│  │  ā”œā”€ rootIndex = 2                      │   │
│  │  ā”œā”€ leftSize = 0                       │   │
│  │  │                                      │   │
│  │  ā”œā”€ LEFT: null                         │   │
│  │  ā”œā”€ RIGHT: null                        │   │
│  │  │                                      │   │
│  │  └─ return TreeNode(15)                │   │
│  │                                         │   │
│  ā”œā”€ 20.left = TreeNode(15)                │   │
│  │                                              │
│  ā”œā”€ RIGHT: build(pre[4-4], in[4-4]) ──────┐  │
│  │                                         │  │
│  │  Level 2: build(pre[4-4], in[4-4])    │  │
│  │  │                                      │  │
│  │  ā”œā”€ rootVal = preorder[4] = 7         │  │
│  │  ā”œā”€ Create node: TreeNode(7)          │  │
│  │  ā”œā”€ rootIndex = 4                      │  │
│  │  ā”œā”€ leftSize = 0                       │  │
│  │  │                                      │  │
│  │  ā”œā”€ LEFT: null                         │  │
│  │  ā”œā”€ RIGHT: null                        │  │
│  │  │                                      │  │
│  │  └─ return TreeNode(7)                 │  │
│  │                                         │  │
│  ā”œā”€ 20.right = TreeNode(7)                │  │
│  │                                              │
│  └─ return TreeNode(20)                        │
│     └─ Node structure:     20                  │
│                            /  \                 │
│                          15    7                │
│                                                  │
ā”œā”€ 3.right = TreeNode(20)                         │
│                                                  │
└─ return TreeNode(3)                             │
   └─ Final tree:       3                         │
                       / \                         │
                      9   20                       │
                         /  \                      │
                       15    7                     │

═══════════════════════════════════════════════════
FINAL RESULT: Complete binary tree constructed! āœ“
═══════════════════════════════════════════════════

Array Indices at Each Step:

LEVEL 0: Root = 3
  preorder indices [0-4]: [3, 9, 20, 15, 7]
  inorder indices [0-4]:  [9, 3, 15, 20, 7]

  Root found at inorder[1]
  Left subtree: 1 element
  Right subtree: 3 elements

LEVEL 1 (Left): Root = 9
  preorder indices [1-1]: [9]
  inorder indices [0-0]:  [9]

  No children → leaf node

LEVEL 1 (Right): Root = 20
  preorder indices [2-4]: [20, 15, 7]
  inorder indices [2-4]:  [15, 20, 7]

  Root found at inorder[3]
  Left subtree: 1 element
  Right subtree: 1 element

LEVEL 2 (Left of 20): Root = 15
  preorder indices [3-3]: [15]
  inorder indices [2-2]:  [15]

  No children → leaf node

LEVEL 2 (Right of 20): Root = 7
  preorder indices [4-4]: [7]
  inorder indices [4-4]:  [7]

  No children → leaf node

šŸ” Visual Index Calculations

Understanding the Index Math:

Given:
  preorder = [3, 9, 20, 15, 7]
  inorder  = [9, 3, 15, 20, 7]
             ↑     ↑
          idx 0   idx 1 (root)

At root (3):
  rootIndex in inorder = 1
  inStart = 0
  leftSize = rootIndex - inStart = 1 - 0 = 1

Preorder partitioning:
  Root: preorder[0] = 3
  Left: preorder[1 to 1+leftSize-1]
      = preorder[1 to 1]
      = [9]
  Right: preorder[1+leftSize to end]
       = preorder[2 to 4]
       = [20, 15, 7]

Inorder partitioning:
  Left: inorder[0 to rootIndex-1]
      = inorder[0 to 0]
      = [9]
  Right: inorder[rootIndex+1 to end]
       = inorder[2 to 4]
       = [15, 20, 7]

Why leftSize is Critical:

leftSize tells us:
  - How many elements in left subtree
  - Where to split preorder array

Without it, we can't partition preorder correctly!

Example:
  inorder  = [4, 2, 5, 1, 3]
             ↑--------↑     ↑
             left    root  right

  leftSize = 3 (three elements before root)

  preorder = [1, 2, 4, 5, 3]
                ↑--------↑  ↑
                left(3)   right

  We need leftSize to know where to split! šŸ”‘

šŸ“Š Complexity Analysis

Time Complexity: O(n)

HashMap construction: O(n)
  Process each element once

Recursive construction: O(n)
  Visit each node once
  O(1) lookup per node (thanks to HashMap)

Total: O(n) āœ“

Space Complexity: O(n)

HashMap: O(n)
  Store all inorder indices

Recursion stack: O(h)
  Best case: O(log n)
  Worst case: O(n)

Total: O(n)


āš ļø Common Mistakes

Mistake 1: Wrong Preorder Partitioning

// āŒ WRONG - Incorrect left subtree range
root.left = build(preorder, preStart, preStart + leftSize,  // WRONG!
                 inorder, inStart, rootIndex - 1);

// Problem: Includes one extra element!
// If leftSize = 1:
//   Should take preorder[1 to 1] (1 element)
//   This takes preorder[1 to 2] (2 elements!) āœ—

// āœ“ CORRECT - Proper range
root.left = build(preorder, preStart + 1, preStart + leftSize,  āœ“
                 inorder, inStart, rootIndex - 1);

Mistake 2: Not Using HashMap

// āŒ WRONG - Linear search each time
private int findRoot(int[] inorder, int val, int start, int end) {
    for (int i = start; i <= end; i++) {
        if (inorder[i] == val) return i;
    }
    return -1;
}

// Time: O(n²) overall āœ—
// Each of n nodes does O(n) search

// āœ“ CORRECT - HashMap for O(1) lookup
Map<Integer, Integer> inorderMap = new HashMap<>();
for (int i = 0; i < inorder.length; i++) {
    inorderMap.put(inorder[i], i);
}
// Time: O(n) overall āœ“

Mistake 3: Wrong Base Case

// āŒ WRONG - Checking array lengths
if (preorder.length == 0) return null;  // WRONG!

// Problem: We pass indices, not subarrays!
// Length is always same, doesn't change!

// āœ“ CORRECT - Check indices
if (preStart > preEnd) return null;  āœ“
// OR
if (inStart > inEnd) return null;  āœ“

Mistake 4: Forgetting to Skip Root in Preorder

// āŒ WRONG - Includes root in left subtree
root.left = build(preorder, preStart, preStart + leftSize,  // Includes root!
                 inorder, inStart, rootIndex - 1);

// āœ“ CORRECT - Skip root (preStart + 1)
root.left = build(preorder, preStart + 1, preStart + leftSize,  āœ“
                 inorder, inStart, rootIndex - 1);

Mistake 5: Wrong leftSize Calculation

// āŒ WRONG - Forgets about inStart
int leftSize = rootIndex;  // WRONG if inStart != 0!

// Example:
//   inStart = 2, rootIndex = 3
//   leftSize should be 1 (one element between 2 and 3)
//   This calculates: 3 āœ—

// āœ“ CORRECT - Subtract inStart
int leftSize = rootIndex - inStart;  āœ“

šŸŽÆ Pattern Recognition - Array Reconstruction Problems

Core Pattern: Divide and Conquer with Properties

Template for reconstruction from traversals:

1. Identify root from one traversal
2. Use another traversal to partition
3. Calculate sizes for partitioning
4. Recursively build subtrees
5. Connect and return

1. Construct Tree from Inorder and Postorder

Similar to this problem!
Difference: Postorder root is LAST element
Partition logic same, just different root position

2. Construct Tree from Preorder (BST)

Only need preorder for BST!
WHY? BST property gives us inorder (sorted)
Use value comparisons to partition

3. Serialize and Deserialize Binary Tree

Encode tree to string
Decode string to tree
Similar reconstruction logic

4. Verify Preorder Serialization

Check if string is valid tree
Use same traversal understanding

When to Use This Pattern:

āœ“ Reconstruct tree from traversals
āœ“ Two arrays with different orders
āœ“ Need to identify structure
āœ“ Divide and conquer applicable
āœ“ HashMap for optimization

šŸ“ Quick Revision Notes

šŸŽÆ Core Concept

Build tree from preorder and inorder arrays. Preorder gives root (first element), inorder partitions left/right (elements before/after root). Use HashMap for O(1) root lookup. Calculate leftSize to partition preorder correctly. Recursively build left and right subtrees with proper index ranges. Divide and conquer!

⚔ Quick Implementation

import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;

class TreeNode {
    int val;
    TreeNode left;
    TreeNode right;

    TreeNode() {}
    TreeNode(int val) { this.val = val; }
    TreeNode(int val, TreeNode left, TreeNode right) {
        this.val = val;
        this.left = left;
        this.right = right;
    }

    public static TreeNode buildTree(Integer[] arr) {
        if (arr == null || arr.length == 0 || arr[0] == null) {
            return null;
        }

        TreeNode root = new TreeNode(arr[0]);
        Queue<TreeNode> queue = new LinkedList<>();
        queue.offer(root);

        int i = 1;
        while (!queue.isEmpty() && i < arr.length) {
            TreeNode current = queue.poll();

            // Process left child
            if (i < arr.length) {
                if (arr[i] != null) {
                    current.left = new TreeNode(arr[i]);
                    queue.offer(current.left);
                }
                i++;
            }

            // Process right child
            if (i < arr.length) {
                if (arr[i] != null) {
                    current.right = new TreeNode(arr[i]);
                    queue.offer(current.right);
                }
                i++;
            }
        }

        return root;
    }

    public static void print(TreeNode root) {
        if (root == null) {
            System.out.println("[]");
            return;
        }

        List<String> result = new ArrayList<>();
        Queue<TreeNode> queue = new LinkedList<>();
        queue.offer(root);

        while (!queue.isEmpty()) {
            TreeNode node = queue.poll();
            if (node != null) {
                result.add(String.valueOf(node.val));
                queue.offer(node.left);
                queue.offer(node.right);
            } else {
                result.add("null");
            }
        }

        while (!result.isEmpty() && result.get(result.size() - 1).equals("null")) {
            result.remove(result.size() - 1);
        }

        System.out.println(result);
    }    
}

class Solution {
  public TreeNode buildTree(int[] preorder, int[] inorder) {
    int preorderStart = 0;
    int preorderEnd = preorder.length - 1;
    int inorderStart = 0;
    int inorderEnd = inorder.length - 1;

    HashMap<Integer, Integer> inorderMap = new HashMap<>();
    for(int i = 0; i < inorder.length; i++) {
      inorderMap.put(inorder[i], i);
    }

    return buildTree(preorderStart, preorderEnd, inorderStart, inorderEnd, inorderMap, preorder, inorder);
  }

  private TreeNode buildTree(int preorderStart, int preorderEnd, int inorderStart, int inorderEnd,
      HashMap<Integer,Integer> inorderMap, int[] preorder, int[] inorder) {
    // base case - boundary checks
    if(preorderStart > preorderEnd || inorderStart > inorderEnd) {
      return null; // this case should never exist and may exist for leaf nodes. Returns null.      
    }

    // step 1: always start element of the preorder array is the root element.
    // why preorderstart instead of 0? It will not be always 0 as we divide array more number of subarrays later in the future.
    int rootElement = preorder[preorderStart];
    // Get index of root element in inorder array.
    // It is guaranteed that all elements are unique.
    // Element before this rootElementIndex belong to LST and after this rootElementIndex belong to RST.
    int rootElementIndex = inorderMap.get(rootElement);

    // Step 2: Initialize root with root element and constrct LST and RST.
    TreeNode root = new TreeNode(rootElement);

    // Step 3: This is the ONLY GOTCHA of this whole problem. How much (size of LST) I have to take in preorder array is the main issue?
    // This is not the issue with inorder array as root element index already decides that.
    int leftSubtreeSize = rootElementIndex - inorderStart;

    // Step 4: Construct LST with the remains of preorder and inorder arrays.
    // All are intuitive if you put on paper except a little care on leftSubtreeSize.
    root.left = buildTree(preorderStart + 1, preorderStart + leftSubtreeSize, inorderStart, rootElementIndex - 1, inorderMap, preorder, inorder);

    // Step 5: Construct RST with the remains of preorder and inorder arrays.
    // All are intuitive if you put on paper
    root.right = buildTree(preorderStart + leftSubtreeSize + 1, preorderEnd, rootElementIndex + 1, inorderEnd, inorderMap, preorder, inorder);

    return root;
  }

  public static void main(String[] args) {
    Solution s = new Solution();

    TreeNode res1 = s.buildTree(new int[] {3,9,20,15,7}, new int[] {9,3,15,20,7});
    TreeNode.print(res1);
  }
}

šŸ”‘ Key Insights

Traversal Properties:

PREORDER (Root → Left → Right):
  [Root, ...Left..., ...Right...]
   ↑
  First element = ROOT!

INORDER (Left → Root → Right):
  [...Left..., Root, ...Right...]
               ↑
  Elements before root = LEFT
  Elements after root = RIGHT

Together → Unique tree! šŸ”‘

The Partitioning Magic:

Given root position in inorder:
  leftSize = rootIndex - inStart

This tells us:
  - How many elements in left subtree
  - Where to split preorder:
    Left:  preorder[preStart+1 to preStart+leftSize]
    Right: preorder[preStart+leftSize+1 to preEnd]

Without leftSize, can't partition preorder! šŸŽÆ

HashMap Optimization:

WITHOUT HashMap:
  For each node: Linear search in inorder
  Time: O(n) Ɨ O(n) = O(n²) āœ—

WITH HashMap:
  Precompute: O(n) once
  Lookup: O(1) per node
  Time: O(n) āœ“

Critical optimization! ⚔

Index Range Pattern:

Always pass indices, not subarrays!

Advantages:
  - No array copying: O(1) space
  - Same arrays throughout
  - Just track ranges

Ranges:
  preorder: [preStart, preEnd]
  inorder:  [inStart, inEnd]

Base case: preStart > preEnd

šŸŽŖ Memory Aid

"Preorder gives ROOT (first element)!"
"Inorder splits LEFT/RIGHT (before/after root)!"
"leftSize = rootIndex - inStart!"
"HashMap for O(1) lookup!" ✨

āš ļø Don't Forget

  • Preorder first = root (always!)
  • Find root in inorder (splits tree!)
  • Calculate leftSize (for partitioning!)
  • Skip root in preorder (preStart + 1!)
  • HashMap for O(1) (not O(n) search!)
  • Pass indices not arrays (avoid copying!)

šŸŽ‰ Congratulations!

You've mastered tree reconstruction from traversals!

What You Learned:

āœ… Traversal properties - What they encode
āœ… Divide and conquer - Recursive partitioning
āœ… HashMap optimization - O(n²) → O(n)
āœ… Index arithmetic - Proper range calculations
āœ… Tree construction - Building from arrays
āœ… Pattern recognition - Similar problems

The Deep Understanding:

Traversals aren't just for printing!
They ENCODE the tree structure!

Preorder + Inorder → Unique tree
Inorder + Postorder → Unique tree
Preorder alone → Not unique (need BST property)

Understanding traversals deeply! 🧠

Interview Mastery:

Interviewer: "Build tree from preorder and inorder"

Your approach:
  "Preorder tells us the root (first element).
   Inorder tells us left vs right split.

   Algorithm:
   1. Root = preorder[0]
   2. Find root in inorder
   3. Calculate leftSize for partitioning
   4. Recursively build left and right

   Optimization: HashMap for O(1) lookup

   Time: O(n), Space: O(n)"

Shows deep understanding! šŸ’Æ

You've learned a fundamental reconstruction algorithm! šŸ†āœØšŸŽÆ