Browse curriculum

House Robber III

Rob a binary tree of houses without robbing two linked ones: a post-order DFS returns, for every house, the best total if you rob it and if you skip it.

Problem Understanding

House Robber III: the houses form a binary tree, each holding some money. Robbing two directly linked houses (a parent and its child) on the same night alerts the police. Return the most money you can rob.

Example: [3,2,3,null,3,null,1] returns 7: rob the root 3 and the two grandchildren 3 and 1.

The Intuition: Two Answers per House

In House Robber each house only clashes with its neighbours in a line. Here a house clashes with its parent and its children, so solve each subtree twice:

  1. rob: this house is robbed, so both children must be skipped: node.val + left.skip + right.skip.
  2. skip: this house is left alone, so each child takes whichever of its two answers is better: max(left) + max(right).
  3. Compute children first (post-order), then the root's max(rob, skip) is the answer.

Every node is visited once. This is dynamic programming on a tree, driven by a depth-first search; House Robber II is the circular variant of the original.

Interactive Walkthrough

Houses fill in from the leaves up; under each finished house is rob/skip. At the end the houses of one best plan turn green. Try Example 2, where skipping the root wins.

Dry Run Table

Input: [3,2,3,null,3,null,1] (root 3; left child 2 with right child 3; right child 3 with right child 1)

House rob skip
leaf 3 (under 2) 3 0
2 2 + 0 + 0 = 2 0 + max(3, 0) = 3
leaf 1 1 0
3 (right child) 3 + 0 + 0 = 3 0 + max(1, 0) = 1
root 3 3 + 3 + 1 = 7 max(2, 3) + max(3, 1) = 6

Answer: max(7, 6) = 7: rob the root and the two grandchildren.

The Solution Template

A post-order DFS returning a pair. C returns the pair through two out-parameters.

House Robber III Code
1var rob = function(root) {
2 const dfs = (node) => {
3 if (!node) return [0, 0];
4 const [lRob, lSkip] = dfs(node.left);
5 const [rRob, rSkip] = dfs(node.right);
6 return [node.val + lSkip + rSkip, Math.max(lRob, lSkip) + Math.max(rRob, rSkip)];
7 };
8 return Math.max(...dfs(root));
9};

Edge Cases & Common Mistakes

  • Skip means "free", not "rob the children": when a house is skipped, each child picks the better of rob and skip; forcing the children to be robbed loses answers.
  • Greedy by level: robbing alternate levels can miss mixed plans like [2,1,3,null,4] (answer 7).
  • Plain recursion without pairs or memo: recomputing grandchildren from every ancestor is exponential.
  • Single house: the answer is its value.
  • Zero-value houses: fine; robbing or skipping them changes nothing.

Summary

Post-order DFS returning (rob, skip) for each subtree: rob = val + left.skip + right.skip, skip = max(left) + max(right). The answer is max of the root's pair. O(n) time, O(h) space.

More practice: DSA interview questions with answers · patterns cheat sheet

The approach, step by step

  1. Recurse post-order

    dfs(node) first computes the (rob, skip) pairs of both children; an empty child is (0, 0).

  2. Combine

    rob = node.val + left.skip + right.skip; skip = max(left) + max(right). Return (rob, skip).

  3. Answer at the root

    Return the larger of the root's two numbers.

Frequently asked questions

What does each node return in House Robber III?

Two numbers for its subtree: rob, the best total when this house is robbed, and skip, the best total when it is not. rob = node.val + left.skip + right.skip, because linked children cannot be robbed. skip = max(left) + max(right), because the children are then free to go either way.


Why is greedy level-by-level robbing wrong?

Robbing alternate levels can miss better mixes. In [2,1,3,null,4] the best plan robs 4 and 3, which sit on different levels; the even levels give 2 + 4 = 6 and the odd level gives 1 + 3 = 4, both below 7.


Why not memoise on the node with a single value?

That works too: rob(node) = max(node.val + grandchildren, children), cached per node. Returning the pair avoids the cache entirely and visits every node exactly once.

Applying for software engineering roles? Free, no sign-up:

AI resume checker — match your resume to the job description