Bird
Raised Fist0
Interview Preptree-dfsmediumAmazonMicrosoftGoogle

Construct Tree from Inorder and Postorder

Choose your preparation mode4 modes available

Start learning this pattern below

Jump into concepts and practice - no test required

or
Recommended
Test this pattern10 questions across easy, medium, and hard to know if this pattern is strong
Steps
setup

Initialize stack and root node

Start by creating the root node from the last element of postorder (3). Push it onto the stack. Set inorder_index to the last index of inorder (4).

💡 This sets the foundation: the root is always the last postorder element, and the stack will help track nodes to attach children.
Line:root_val = postorder[-1] root = TreeNode(root_val) stack.append(root) inorder_index = len(inorder) - 1
💡 Root node identified and stack initialized to manage subtree construction.
📊
Construct Tree from Inorder and Postorder - Watch the Algorithm Execute, Step by Step
Watching this step-by-step reveals how the stack helps reconstruct the tree without recursion, clarifying the relationship between inorder and postorder traversals.
Step 1/10
·Active fillAnswer cell
Current node: 0
3
Current node: 1
320
Current node: 2
3207
Current node: 3
320715
Current node: 3
320715
Current node: 3
320715
Current node: 4
3207159
Current node: 4
3207159
3207159
Current node: 0
3207159
Return: 0

Key Takeaways

The last element in postorder is always the root, which anchors the entire tree construction.

This is fundamental but often overlooked; seeing it visually helps cement this root identification.

The stack tracks nodes whose subtrees are not fully constructed, enabling iterative backtracking instead of recursion.

Understanding how the stack simulates recursion clarifies the algorithm's flow and why nodes are popped when inorder matches.

When the top of the stack matches the current inorder element, it signals completion of the right subtree and triggers attaching left children.

This decision point is subtle in code but critical; the visualization makes it explicit and easy to follow.

Practice

(1/5)
1. Given the following Morris preorder traversal code, what is the final output list after running preorderTraversal on the tree below? Tree structure:

    1
   / \
  2   3
def preorderTraversal(root):
    result = []
    current = root
    while current:
        if current.left is None:
            result.append(current.val)
            current = current.right
        else:
            predecessor = current.left
            while predecessor.right and predecessor.right != current:
                predecessor = predecessor.right
            if predecessor.right is None:
                predecessor.right = current
                result.append(current.val)
                current = current.left
            else:
                predecessor.right = None
                current = current.right
    return result
easy
A. [2, 1, 3]
B. [1, 2, 3]
C. [1, 3, 2]
D. [3, 1, 2]

Solution

  1. Step 1: Trace first iteration with current=1

    Node 1 has left child 2, predecessor is 2 with no right child, set 2.right=1, append 1, move current=2.
  2. Step 2: Trace second iteration with current=2

    Node 2 has no left child, append 2, move current=2.right which points back to 1 (thread).
  3. Step 3: Detect thread at 2.right=1

    Since predecessor.right == current, reset 2.right=None, move current=1.right=3.
  4. Step 4: Trace current=3

    Node 3 has no left child, append 3, move current=3.right=None, loop ends.
  5. Final Answer:

    Option B -> Option B
  6. Quick Check:

    Output matches preorder traversal [1,2,3] [OK]
Hint: Morris preorder appends root before left subtree [OK]
Common Mistakes:
  • Appending nodes in wrong order
  • Not resetting threaded links
  • Confusing left and right child traversal
2. Consider the following iterative postorder traversal code to compute the diameter of a binary tree. Given the tree: 1 / \ 2 3 / 4 What is the final value of the variable diameter after the function completes?
easy
A. 2
B. 4
C. 1
D. 3

Solution

  1. Step 1: Trace heights and diameter updates for each node.

    Node 4: height=1, diameter=0; Node 2: left=1 (node4), right=0, diameter=max(0,1)=1; Node 3: height=1, diameter=1; Node 1: left=2 (node2), right=1 (node3), diameter=max(1,2+1=3)=3.
  2. Step 2: Verify final diameter value returned.

    The diameter is the maximum sum of left and right heights at any node, which is 3 edges, so the longest path length is 3 edges.
  3. Final Answer:

    Option D -> Option D
  4. Quick Check:

    Longest path is from node 4 to node 3 with length 3 edges [OK]
Hint: Diameter counts edges, not nodes [OK]
Common Mistakes:
  • Confusing diameter as number of nodes
  • Off-by-one in height calculation
  • Missing right subtree height
3. Consider the following Python code implementing the optimal flatten function for a binary tree. Given the input tree: 1 / \ 2 3 What is the value of the global variable prev after the call flatten(root) completes?
easy
A. TreeNode with val=3
B. TreeNode with val=1
C. TreeNode with val=2
D. None

Solution

  1. Step 1: Trace flatten calls on root=1

    flatten(1) calls flatten(3) then flatten(2). After flatten(3), prev=TreeNode with val=3; after flatten(2), prev=TreeNode with val=2; finally, root=1 sets root.right=prev (2) and prev=TreeNode with val=1.
  2. Step 2: Final value of prev after flatten(1)

    After processing root=1, prev points to the root node with val=1.
  3. Final Answer:

    Option B -> Option B
  4. Quick Check:

    Global prev ends at root node after full traversal [OK]
Hint: Global prev ends at root after full flatten [OK]
Common Mistakes:
  • Assuming prev ends at last leaf node
  • Confusing order of recursive calls
  • Forgetting prev is updated after rewiring
4. Examine the following buggy code for the Path Sum problem. Which line contains the subtle bug that causes incorrect results on some inputs?
def hasPathSum(root: Optional[TreeNode], targetSum: int) -> bool:
    def dfs(node, curr_sum):
        if not node:
            return False
        curr_sum += node.val
        if curr_sum == targetSum:  # Bug here
            return True
        return dfs(node.left, curr_sum) or dfs(node.right, curr_sum)
    return dfs(root, 0)
medium
A. Line 3: if not node: return False
B. Line 6: if curr_sum == targetSum:
C. Line 8: return dfs(node.left, curr_sum) or dfs(node.right, curr_sum)
D. Line 5: curr_sum += node.val

Solution

  1. Step 1: Understand leaf node condition

    The sum should be checked only at leaf nodes, not at every node.
  2. Step 2: Identify the bug

    Line 6 checks sum at any node, causing premature True returns for partial paths.
  3. Final Answer:

    Option B -> Option B
  4. Quick Check:

    Bug causes incorrect True if partial path sum equals targetSum [OK]
Hint: Sum check must be at leaf nodes only [OK]
Common Mistakes:
  • Checking sum at internal nodes
  • Not handling null root
5. What is the time complexity of the BFS-based serialization and deserialization of a binary tree with n nodes, assuming the tree is balanced? Consider both serialization and deserialization separately.
medium
A. Serialization: O(n log n), Deserialization: O(n log n)
B. Serialization: O(n), Deserialization: O(n^2)
C. Serialization: O(n), Deserialization: O(n)
D. Serialization: O(n^2), Deserialization: O(n)

Solution

  1. Step 1: Analyze serialization time

    Each node is enqueued and dequeued exactly once, so serialization is O(n).
  2. Step 2: Analyze deserialization time

    Deserialization processes each node once, reconstructing the tree in O(n) time.
  3. Final Answer:

    Option C -> Option C
  4. Quick Check:

    Both serialization and deserialization scale linearly with number of nodes [OK]
Hint: Each node is processed once in BFS serialization/deserialization [OK]
Common Mistakes:
  • Assuming deserialization is O(n^2) due to nested loops
  • Confusing tree height with number of nodes
  • Thinking serialization is O(n log n) due to queue operations