Bird
Raised Fist0
Interview Prepdp-advanced-trees-bitmaskmediumAmazonGoogle

House Robber III (On Tree)

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 DP Table and Start Post-Order Traversal

We start with an empty DP table for all nodes. No DP values are computed yet, so all cells are '?'. The traversal will process nodes from leaves up to the root.

💡 Initialization sets the stage for bottom-up computation, ensuring no values are assumed before processing children.
Line:def rob(root): def dfs(node): if not node: return (0, 0)
💡 DP values must be computed bottom-up; leaves first, then parents.
📊
House Robber III (On Tree) - Watch the Algorithm Execute, Step by Step
Watching the algorithm step-by-step reveals how the tree structure influences the DP decisions and why robbing adjacent nodes is forbidden.
Step 1/10
·Active fillAnswer cell
Initializing dp array
i\w01
i=0??
i=1??
i=2??
i=3??
i=4??
uncomputed
Item 3 - wt:0 val:3
i\w01
i=0??
i=1??
i=2??
i=303
i=4??
leaf node computed
Item 4 - wt:0 val:1
i\w01
i=0??
i=1??
i=2??
i=303
i=401
leaf node computed
Item 1 - wt:0 val:2
i\w01
i=0??
i=132
i=2??
i=303
i=401
node computed
Item 2 - wt:0 val:3
i\w01
i=0??
i=132
i=213
i=303
i=401
node computed
Item 0 - wt:0 val:3
i\w01
i=067
i=132
i=213
i=303
i=401
root computed
Item 0 - wt:0 val:7
i\w01
i=067
i=132
i=213
i=303
i=401
final answer
Item 0 - wt:0 val:7
i\w01
i=067
i=132
i=213
i=303
i=401
robbed root
Initializing dp array
i\w01
i=067
i=132
i=213
i=303
i=401
computed
Item 0 - wt:0 val:7
i\w01
i=067
i=132
i=213
i=303
i=401
answer cell

Key Takeaways

DP on trees requires bottom-up post-order traversal to ensure children are processed before parents.

This traversal order is crucial because parent's DP values depend on children's results, which is not obvious from code alone.

Each node stores two DP states: rob and not_rob, representing mutually exclusive choices.

Understanding these two states clarifies why we cannot rob adjacent nodes and how the algorithm enforces this constraint.

The final answer is the max of rob and not_rob at the root, reflecting the best overall choice.

Seeing the final comparison visually helps students grasp how the DP values translate to the solution.

Practice

(1/5)
1. You need to visit all nodes of a binary tree in the order: root node first, then recursively the left subtree, followed by the right subtree. Which algorithmic approach guarantees this traversal order efficiently without extra space for recursion or stack?
easy
A. Postorder traversal using two stacks
B. Level-order traversal using a queue
C. Morris preorder traversal using threaded binary tree
D. Inorder traversal using recursion

Solution

  1. Step 1: Understand traversal order requirement

    The problem requires visiting root before left and right subtrees, which is preorder traversal.
  2. Step 2: Identify approach with no extra space

    Morris preorder traversal uses threaded binary trees to achieve preorder traversal without recursion or stack, thus optimal in space.
  3. Final Answer:

    Option C -> Option C
  4. Quick Check:

    Preorder visits root first; Morris traversal achieves this with O(1) space [OK]
Hint: Preorder visits root before children [OK]
Common Mistakes:
  • Confusing inorder or postorder with preorder traversal
  • Assuming recursion is always needed
  • Using level-order which visits nodes by depth
2. Given a binary tree, you need to find the length of the longest path between any two nodes in the tree. This path may or may not pass through the root. Which approach guarantees an optimal solution to this problem?
easy
A. Perform a postorder traversal computing heights and update the diameter at each node using recursion with a global variable.
B. Apply dynamic programming with memoization on node pairs to find longest paths.
C. Use a greedy traversal that always chooses the deeper subtree at each node.
D. Calculate the height of the tree and multiply by two to estimate the diameter.

Solution

  1. Step 1: Understand the problem requires the longest path between any two nodes, not necessarily passing through root.

    The diameter can be found by checking the sum of left and right subtree heights at every node.
  2. Step 2: Recognize that a postorder traversal with recursion and a global variable to track diameter updates at each node ensures all paths are considered.

    This approach visits each node once, updating the diameter with left_height + right_height.
  3. Final Answer:

    Option A -> Option A
  4. Quick Check:

    Postorder traversal with global diameter update covers all nodes [OK]
Hint: Diameter updates at every node, not just root [OK]
Common Mistakes:
  • Thinking diameter must pass through root
  • Using greedy to pick deeper subtree only
  • Confusing diameter with twice height
3. Given a binary tree and a target sum, you need to count all paths where the sum of the node values along the path equals the target. The path can start and end at any node but must go downwards (parent to child). Which approach guarantees an optimal solution in terms of time complexity?
easy
A. Use a brute force DFS starting from every node, checking all downward paths for the target sum.
B. Use dynamic programming to store sums of subtrees and combine results bottom-up.
C. Use a DFS with a prefix sum hash map to track cumulative sums and count valid paths in one traversal.
D. Use a greedy approach to prune paths that exceed the target sum early.

Solution

  1. Step 1: Understand problem constraints and path definition

    The path can start and end anywhere but must go downwards, so checking all paths naively is expensive.
  2. Step 2: Recognize prefix sum with DFS pattern

    Using a prefix sum hash map during DFS allows counting all valid paths in O(n) time by tracking cumulative sums and their frequencies.
  3. Final Answer:

    Option C -> Option C
  4. Quick Check:

    Prefix sum + DFS solves in linear time [OK]
Hint: Prefix sums track cumulative sums efficiently [OK]
Common Mistakes:
  • Assuming paths must start at root or leaf
  • Using brute force DFS from every node without pruning
  • Trying greedy pruning which fails on negative values
4. Suppose the problem changes: the binary tree is no longer complete but only a general binary tree. Which of the following statements about counting nodes efficiently is true?
hard
A. Breadth-first search (BFS) can count nodes in O(log n) time for any binary tree.
B. The optimal binary search approach on the last level still applies with minor modifications.
C. Using tree height to check perfect subtrees can still reduce time complexity to O((log n)^2).
D. A simple DFS traversal counting all nodes is the only guaranteed correct method with O(n) time.

Solution

  1. Step 1: Understand the problem change

    The tree is no longer complete, so properties used by optimal methods do not hold.
  2. Step 2: Identify correct counting method

    Only a full traversal (DFS or BFS) guarantees counting all nodes correctly in O(n) time.
  3. Final Answer:

    Option D -> Option D
  4. Quick Check:

    Optimal methods rely on completeness, which is lost here [OK]
Hint: Without completeness, must traverse all nodes [OK]
Common Mistakes:
  • Trying to apply binary search on last level
  • Assuming height checks still reduce complexity
5. Suppose the binary tree nodes have parent pointers already, but the tree is very deep (height h). You want to find the Lowest Common Ancestor of two nodes p and q. Which approach is best to optimize time and space, and why?
hard
A. Use brute force by checking all ancestors of p and q repeatedly until common ancestor is found.
B. Use the parent pointer + ancestor set approach, building a set for p's ancestors and then traversing q's ancestors.
C. Use recursive DFS from root to find paths to p and q, then compare paths to find LCA.
D. Use the parent pointer + two-pointer technique: move the deeper node up until both nodes are at the same depth, then move both up simultaneously until they meet.

Solution

  1. Step 1: Understand problem constraints

    Nodes have parent pointers, tree is deep, so space usage matters.
  2. Step 2: Evaluate approaches

    Ancestor set approach (B) uses O(h) space. Recursive DFS (C) and brute force (D) are inefficient for deep trees.
  3. Step 3: Two-pointer technique

    Moving deeper node up to equalize depth, then moving both up simultaneously uses O(1) space and O(h) time, optimal for deep trees.
  4. Final Answer:

    Option D -> Option D
  5. Quick Check:

    Two-pointer approach optimizes space and time for deep trees with parent pointers [OK]
Hint: Two-pointer approach uses O(1) space for LCA with parent pointers [OK]
Common Mistakes:
  • Using ancestor sets unnecessarily
  • Recursing from root wasting time
  • Brute force repeated ancestor checks