Browse curriculum

House Robber

Maximise the money robbed from a row of houses without robbing two adjacent ones, with a one-dimensional DP.

Problem Understanding

House Robber: houses along a street hold nums[i] money each. Robbing two adjacent houses sets off an alarm. Return the most money you can rob without robbing two neighbours.

Example: [2, 7, 9, 3, 1] returns 12: houses 0, 2 and 4 (2 + 9 + 1).

Attempt 1: Try Every Choice

At each house, recurse twice: once skipping it, once robbing it and jumping two houses ahead. That explores every valid plan — O(2ⁿ) — and solves the same suffix of the street over and over. The repeated subproblems are the sign that dynamic programming applies.

The Intuition: Skip or Rob, One House at a Time

Let dp[i] be the most money from the first i houses. For the last of them, house i - 1, there are only two options:

  1. Skip it: the best is whatever the first i - 1 houses gave — dp[i - 1].
  2. Rob it: its neighbour is off-limits, so add nums[i - 1] to the best of the first i - 2 houses — dp[i - 2] + nums[i - 1].

So dp[i] = max(dp[i - 1], dp[i - 2] + nums[i - 1]), starting from dp[0] = 0 and dp[1] = nums[0]. Fill the table left to right; dp[n] is the answer. Each cell reads only the two before it, which is why two variables are enough in the optimised version.

Interactive Walkthrough

dp[i] = MOST MONEY FROM THE FIRST i HOUSES

BASE CASES

nums

2

0

7

1

9

2

3

3

1

4

dp

0

0

2

1

·

2

·

3

·

4

·

5

Skip

—

Rob

—

Best

—

 

dp[0] = 0 (no houses). dp[1] = 2: with one house, rob it

Next

House 1 holds 7. Skip it: dp[1] = 2. Rob it: dp[0] + 7 = 7

Houses

The houses run along the top and the dp table under them. For each house, the two cells the choice reads — dp[i − 1] to skip and dp[i − 2] to rob — are marked, and the winner fills dp[i]. The last step walks the table backwards to light up the houses the best plan robs.

O(1) Space: Keep Only Two Values

Each dp[i] reads only dp[i − 1] and dp[i − 2], so the table can shrink to two variables:

def rob(nums):
    prev2, prev1 = 0, 0
    for num in nums:
        prev2, prev1 = prev1, max(prev1, prev2 + num)
    return prev1

Same O(n) time, O(1) space. The table version is easier to trace — and to reconstruct which houses were robbed — which is why the walkthrough uses it.

The approach, step by step

  1. Set the base cases

    dp[0] = 0 and dp[1] = nums[0].

  2. Skip or rob

    For each later house, dp[i] = max(dp[i - 1], dp[i - 2] + nums[i - 1]).

  3. Return the last cell

    dp[n] is the most money from all n houses.

Frequently asked questions

What is the recurrence for House Robber?

Let dp[i] be the most money from the first i houses. At house i - 1 you either skip it, keeping dp[i - 1], or rob it and add it to dp[i - 2], the best that ends at least one house earlier. dp[i] = max(dp[i - 1], dp[i - 2] + nums[i - 1]), with dp[0] = 0 and dp[1] = nums[0].


Can House Robber be solved in O(1) space?

Yes. Each dp value only reads the previous two, so two variables are enough: keep prev2 and prev1, and on each house set them to prev1 and max(prev1, prev2 + num). The time stays O(n).


Why not just rob every other house?

Alternating is not always best. In [2, 1, 1, 2], robbing houses 0 and 2 or 1 and 3 gives 3, but robbing houses 0 and 3 gives 4. The DP compares both choices at every house instead of fixing a pattern.