Sale: Get 60% Off on all Pro Plans. Buy Now

House Robber

Problem

Given an integer array nums representing the amount of money of each house, return the maximum amount of money you can rob tonight without alerting the police, where adjacent houses cannot be robbed on the same night.

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

Example

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

The brute-force approach tries all combinations of robbing or skipping houses, which is exponential. The optimal strategy uses recursion with memoization to avoid redundant calculations. Starting from the last house (index 4), the algorithm decides whether to rob it plus the best from houses before the adjacent one (index 2), or skip it and take the best from the previous house (index 3). For example, robbing house 4 (value 1) plus dp(2) (which is 11) yields 12, while skipping house 4 and taking dp(3) (which is 9) yields 9. The maximum is 12. This decision process repeats recursively, caching results to avoid recomputation.

Approach

Straightforward Solution

A brute-force recursive approach tries all subsets of houses to rob, leading to exponential time complexity due to repeated calculations of the same subproblems.

Core Observation

The maximum amount that can be robbed up to house i depends only on the decisions made for houses before i, specifically whether house i-1 was robbed or not, creating overlapping subproblems with optimal substructure.

Path to Optimal

Preview

Recognizing the overlapping subproblems and optimal substructure leads to a dynamic programming solution…

Full step-by-step walkthrough on Pro

Optimal Approach

Preview

Implement a top-down recursive function with memoization that returns dp(i), the maximum amount that can be robbed up to house i. The function returns 0 for negative indices (base case)…

Full step-by-step walkthrough on Pro

Want the full reasoning chain?

Unlock the complete walkthrough, line-by-line analysis, and recall drill.

Unlock Pro

Time

O(n)

Each house index from 0 to n-1 is computed once and stored in the memo dictionary, avoiding repeated calculations.

Space

O(n)

The memo dictionary stores results for each house index, and the recursion stack can go up to O(n) in the worst case.

Pattern Spotlight

Dynamic Programming (Top-Down Memoization)

When a problem involves making a sequence of decisions with overlapping subproblems and optimal substructure, define a recursive state representing the problem up to a certain index, then cache results to avoid redundant work and achieve linear time.

Solution

Python
1class Solution:
2 def rob(self, nums: list[int]) -> int:
3 memo = {}
4
5 def dp(i):
6 if i < 0:
7 return 0
8 if i in memo:
9 return memo[i]
10
11 result = max(nums[i] + dp(i - 2), dp(i - 1))
12 memo[i] = result
13 return result
14
15 return dp(len(nums) - 1)

Step-by-Step Solution

1

Initialize Memoization Cache for Subproblem Results

3memo = {}

Objective

To create a dictionary that stores the maximum robbery amount for each house index to avoid redundant recursive computations.

Key Insight

Memoization transforms the naive exponential recursion into a linear time algorithm by caching results of subproblems. This cache acts as a memory of previously solved states, ensuring each state is computed once and reused whenever needed.

Interview Quick-Check

Core Logic

Memoization stores dp(i) results, preventing repeated calculations of the same subproblems.

Common Pitfalls & Bugs

Forgetting to memoize results leads to exponential time due to repeated recursive calls.

2

Define Recursive Function to Compute Maximum Robbery Up to Index i

To recursively compute the maximum amount that can be robbed from houses 0 to i, using memoization to cache results.

3

Return Maximum Robbery Amount for All Houses

To initiate the recursive computation from the last house and return the final maximum robbery amount.

2 more steps with full analysis available on Pro.

Line Analysis

This solution has 3 Critical lines interviewers watch for.

Line 9 Critical
return memo[i]

Compute the maximum robbery amount by choosing to rob house i plus dp(i-2) or skip house i and take dp(i-1).

This recurrence captures the core decision: robbing house i excludes adjacent house i-1, so the next valid house is i-2; skipping house i allows considering house i-1. Taking the max ensures optimality.

Line 6 Critical
if i < 0:

Return 0 if the index i is negative, representing no houses left to rob.

This base case prevents invalid indices and terminates recursion, ensuring correctness and preventing infinite recursion.

Line 7 Critical
return 0

Check if the result for index i is already computed and cached.

This memoization check avoids recomputing dp(i), which is critical for achieving linear time complexity.

Full line-by-line criticality + rationale for all 9 lines available on Pro.

Test Your Understanding

Why does the recursive function consider dp(i - 2) when deciding to rob house i?

See the answer with Pro.

Related Problems

Dynamic Programming pattern

Don't just read it. Drill it.

Reconstruct House Robber from memory until it sticks. AlgoDrill blanks out key lines and makes you fill them back in, step by step.

Unlock the House Robber drill

or drill a free problem