AlgoMaster Logo

House Robber

mediumFrequency8 min readUpdated June 23, 2026

Understanding the Problem

We have a row of houses, each with some amount of money. We want to maximize the total money we collect, but we cannot rob two houses that are next to each other. So if we rob house i, we must skip house i+1 (and house i-1).

This is not as simple as "take every other house." Consider [2, 1, 1, 100]. If you greedily take house 0 and house 2 (skipping odd indices), you get 2 + 1 = 3. But the optimal is house 0 and house 3, giving 2 + 100 = 102. The decision of whether to rob a house depends on decisions made for earlier houses.

At each house, you have exactly two choices. Rob it, which means adding its value to the best you could do up to two houses back, or skip it, which means carrying forward the best you could do up to the previous house. Picking the larger of these two options at every house is what this problem reduces to, and that "take or skip with a neighbor constraint" structure is a dynamic programming setup.

Key Constraints:

  • 1 <= nums.length <= 100 --> The array always has at least one house, so we never handle an empty input. Each house is either robbed or not, so there are up to 2^100 possible selections, which rules out testing every combination by brute force. An O(n) dynamic programming solution handles the full range comfortably.
  • 0 <= nums[i] <= 400 --> Values are non-negative, so robbing a house never reduces the total. The maximum possible answer is 100 * 400 = 40000, which fits in a 32-bit integer with no overflow risk.

Approach 1: Recursion (Brute Force)

Intuition

We can express the answer through a recursive decision at each house: rob it or skip it.

If we rob house i, we earn nums[i] and then solve the subproblem for houses 0 through i-2, since house i-1 must be skipped. If we skip house i, we solve the subproblem for houses 0 through i-1. The answer for house i is the larger of these two results.

This recursion models the problem directly, but it is slow because the same subproblem gets solved repeatedly. Computing the answer for house 5 requires the answers for houses 3 and 4, and house 4 in turn requires house 3. Because these subproblems overlap, the recursion tree branches into roughly two children at every level, so the number of calls grows exponentially.

Algorithm

  1. Define a recursive function rob(i) that returns the maximum money from houses 0 through i.
  2. Base cases: if i < 0, return 0. If i == 0, return nums[0].
  3. Recursive case: return max(rob(i - 1), nums[i] + rob(i - 2)).
  4. Call rob(n - 1) where n is the length of nums.

Example Walkthrough

1rob(4): consider house 4 (val=1). Choose max of rob(3) vs 1+rob(2)
0
2
1
7
2
9
3
3
4
1
rob(4)
1/6

Code

The recursion recomputes the same subproblems many times. Storing the result of each rob(i) the first time it is computed removes that wasted work, which is the next approach.

Approach 2: Memoization (Top-Down DP)

Intuition

The brute force recursion does too much redundant work, which we fix by caching results. The first time we compute rob(i), we store the answer. Every later call to rob(i) returns the cached value immediately.

This technique is called memoization. Each subproblem rob(0) through rob(n-1) is now solved exactly once, and each does O(1) work: one comparison and one addition. The total work drops from O(2^n) to O(n).

Algorithm

  1. Create a memo array of size n, initialized to -1 (meaning "not computed yet").
  2. Define rob(i) the same as before, but before computing, check if memo[i] is already set. If so, return it.
  3. After computing, store the result in memo[i] before returning.
  4. Call rob(n - 1).

Example Walkthrough

1Initialize memo = [-1, -1, -1, -1, -1]. Start rob(4).
0
-1
1
-1
2
-1
3
-1
4
-1
1/6

Code

This uses O(n) space for the memo array plus O(n) for the recursion stack. Since rob(i) depends only on rob(i-1) and rob(i-2), the answers can be filled in iteratively from left to right, which removes the recursion entirely.

Approach 3: Tabulation (Bottom-Up DP)

Intuition

Instead of starting from the last house and recursing backward, we reverse the direction. Start from the first house and work forward. For each house i, compute the best we can do considering houses 0 through i. The recurrence is the same: dp[i] = max(dp[i-1], nums[i] + dp[i-2]). Filling the table iteratively removes the recursion overhead and makes the computation order explicit.

This bottom-up form is easy to analyze and carries no risk of stack overflow on a long input, since there are no recursive calls at all.

Algorithm

  1. If n == 1, return nums[0].
  2. Create an array dp of size n.
  3. Set dp[0] = nums[0] (only one house, rob it).
  4. Set dp[1] = max(nums[0], nums[1]) (pick the richer of the first two houses).
  5. For i from 2 to n-1: dp[i] = max(dp[i-1], nums[i] + dp[i-2]).
  6. Return dp[n-1].

Example Walkthrough

1Input: nums = [2, 7, 9, 3, 1]. Initialize dp array.
0
0
1
0
2
0
3
0
4
0
1/7

Code

This allocates an array of size n, but at any step only the last two values are read. The dp array can be replaced with two variables, which is the final approach.

Approach 4: Space-Optimized DP

Intuition

The recurrence dp[i] = max(dp[i-1], nums[i] + dp[i-2]) reads only two past values: the result one step back and the result two steps back. Once we move past house i, the value at dp[i-2] is never read again. So instead of the full dp array, we keep two variables: prev1 for the best up to the previous house and prev2 for the best up to two houses back.

At each house we compute the new best, then shift the variables so the old prev1 becomes the new prev2 and the newly computed value becomes prev1. This produces the same sequence of values as the tabulation array while bringing space from O(n) down to O(1).

Algorithm

  1. If n == 1, return nums[0].
  2. Initialize prev2 = nums[0] and prev1 = max(nums[0], nums[1]).
  3. For i from 2 to n-1:
    • Compute current = max(prev1, nums[i] + prev2).
    • Update: prev2 = prev1, prev1 = current.
  4. Return prev1.

Example Walkthrough

1Input: nums = [2, 7, 9, 3, 1]. Initialize prev2=2, prev1=7
0
2
prev2=2
1
7
prev1=7
2
9
3
3
4
1
1/5

Code