2Brute2Best
🔥0

House Robber

Medium·LeetCode #198Basic Anchor
LeetCode
30-Second Mental ModelTake or skip (dp[i] = max(skip, take))
Complexity:O(2^N)→O(N)
Key Insight

Let dp[i] be the best total for houses 0..i. Then dp[i] = max(dp[i−1], dp[i−2] + nums[i]). Skip house i keeps dp[i−1]. Take house i adds it to dp[i−2]. Each step needs only the two values before it, so two variables are enough.

Core Validity Invariant

Taking house i is legal only when we add it to dp[i − 2], because dp[i − 2] never includes house i − 1. Adding it to dp[i − 1] could put two neighbours together.

01

Problem

A street has a row of houses, and each house holds some money. You want to rob as much as you can, but there is one rule: if you rob two houses that sit next to each other, the alarm goes off.

Return the most money you can take without robbing two neighbouring houses.

Input

nums, a list of whole numbers. nums[i] is the money in house i.

Output

The largest total you can rob, with no two neighbouring houses both robbed.

Example 1
Input: nums = [1, 2, 3, 1]
Output: 4

Rob house 0 (1) and house 2 (3). Total 4. Robbing houses 1 and 2 is not allowed, and robbing houses 1 and 3 gives only 2 + 1 = 3.

Example 2
Input: nums = [2, 7, 9, 3, 1]
Output: 12

Rob houses 0, 2 and 4: 2 + 9 + 1 = 12. No other safe set of houses gives more.

Example 3
Input: nums = [5]
Output: 5

There is only one house, so rob it.

Constraints
  • • 1 ≤ nums.length ≤ 100
  • • 0 ≤ nums[i] ≤ 400
02

First, think yourself

Before opening any solution, try to answer these on paper. Use hints only when stuck — each hint is counted.

  1. 1.

    If I rob house 3, which other houses can I no longer rob?

  2. 2.

    Does the best answer for houses 0 to 4 depend on the best answer for houses 0 to 3, or only on some of the earlier ones?

  3. 3.

    If I know the best answer for the first few houses, what new choice do I have when I look at one more house?

  4. 4.

    What should the answer be when there are no houses at all?

  5. 5.

    Is the answer always the highest-money houses? Try a small row where skipping a big house is the better move.

03

Brute force approach

04

Better approach

05

Optimized approach

06

Why does this algorithm work?

07

Dry run

08

All approaches compared

ApproachIdeaTimeSpace
Brute ForceRecurse from the last house: take it (look back 2) or skip it (look back 1)O(2^N)O(N)
BetterStore each best(i) the first time; later calls return the stored valueO(N)O(N)
OptimalKeep the last two answers and roll them forwardO(N)O(1)
09

Why does this belong to this pattern?

Clues in the problem statement
  • “take or skip each house”Each house is a yes or no choice, and the answer depends on the choices made so far.
  • “two neighbouring houses cannot both be robbed”A taken house blocks the one before it, so the look-back is 2 steps, not 1.
  • “the most money”At each house keep the better of two options. This is a maximum that carries forward.
Take or skip each item+Taking one blocks its neighbour+Maximize the total→Take or skip (dp[i] = max(skip, take))

This is the anchor of the take-or-skip family. At each house the rule is take it and the house before it is out, or skip it. House Robber II changes only the two ends, which touch each other on a ring. Delete and Earn keeps this same rule but applies it to values instead of positions.

10

Don't make this mistake

Pattern confusion: splitting by index parity
✗ int even = 0, odd = 0; for (int i = 0; i < n; i++) (i % 2 == 0 ? even : odd) += nums[i]; return max(even, odd);
✓ int cur = max(prev1, prev2 + x); prev2 = prev1; prev1 = cur;

Houses with the same parity never touch, so each pile is a safe set. But the best set often mixes both piles. On the dry run [5, 1, 1, 9, 1, 2, 7] the even indices sum to 14 and the odd indices to 12, so the shortcut returns 14. The real answer is 21, from houses 0, 3 and 6 (5 + 9 + 7). Even [2, 1, 1, 2] fails: the shortcut gives 3, but houses 0 and 3 give 4. Only neighbours matter, not index parity.

Adding to the wrong neighbour
✗ int cur = max(prev1, prev1 + x);
✓ int cur = max(prev1, prev2 + x);

prev1 is the best up to the previous house, so it may already include house i − 1. On the dry run [5, 1, 1, 9, 1, 2, 7], house 1 gives max(5, 5 + 1) = 6, which robs houses 0 and 1 together. They are neighbours, so that is not allowed.

Greedy: rob the biggest house first

Sorting by money and grabbing the biggest house fails when a big house blocks two medium ones. On [4, 5, 4] the greedy takes the 5 in the middle and then cannot take either 4, so it gets 5. Robbing both 4s gives 8. The take-or-skip rule has to be checked house by house, in order.

Returning prev2 instead of prev1
✗ return prev2;
✓ return prev1;

With nums = [5], one loop step leaves prev1 = 5 and prev2 = 0. Returning prev2 gives 0, but the answer is 5. prev1 always holds the latest house.

11

Want to implement this?

Write it yourself on LeetCode, then come back and compare your code with the solutions above.

Open coding workspace
How did it go?
Beyond The LeetCode Problem

How this concept connects to System Design & C++ Systems

Bridge DSA with production engineering

Top engineering interviews test whether you understand how algorithms like DP — 1D Linear translate into production architectures and low-level memory mechanics.