150NeetCode 150

101.House Robber

MediumBlind 751-D Dynamic Programming ยท 3/12

Problem

Houses along a street each hold some money, given as nums[i]. You want to rob as much as possible, but two adjacent houses cannot both be robbed โ€” their alarms are linked.

Return the maximum amount of money you can rob without robbing two neighbouring houses.

Examples

Input:  nums = [1, 2, 3, 1]
Output: 4
Explanation: Rob house 0 (1) and house 2 (3): 1 + 3 = 4.
Input:  nums = [2, 7, 9, 3, 1]
Output: 12
Explanation: Rob houses 0, 2 and 4: 2 + 9 + 1 = 12.
Input:  nums = [2, 1, 1, 2]
Output: 4
Explanation: Rob houses 0 and 3 โ€” skipping two in a row is allowed.

Constraints

  • 1 <= nums.length <= 100
  • 0 <= nums[i] <= 400

๐Ÿ’ก Key Insight

At each house i there are only two choices: skip it (keep the best answer for the first i - 1 houses) or rob it (add nums[i] to the best answer for the first i - 2 houses). So best(i) = max(best(i - 1), nums[i] + best(i - 2)). The state only looks back two positions, so the whole DP collapses into two rolling variables.

Recursion (Brute Force)

TimeO(2โฟ)SpaceO(n)

Intuition

Walk from the last house backwards. For house i, either rob it (then house i - 1 is off-limits, continue at i - 2) or skip it (continue at i - 1). Return the better of the two.

Recurrence: f(i) = max(f(i - 1), nums[i] + f(i - 2)), with f(i) = 0 for i < 0.

Algorithm

  1. dfs(i): if i < 0 return 0.
  2. Return max(dfs(i - 1), nums[i] + dfs(i - 2)).
  3. Answer is dfs(n - 1).

Visualization

nums = [2, 7, 9, 3]. f(1) is computed twice and f(0) three times.

flowchart TD
    A["f(3)"] -->|"skip 3"| B["f(2)"]
    A -->|"rob 3"| C["3 + f(1)"]
    B -->|"skip 2"| D["f(1)"]
    B -->|"rob 2"| E["9 + f(0)"]
    C -->|"skip 1"| F["f(0)"]
    C -->|"rob 1"| G["7 + f(-1)"]
    D -->|"skip 1"| H["f(0) = 2"]
    D -->|"rob 1"| I["7 + f(-1) = 7"]

f(0) = 2, f(1) = 7, f(2) = max(7, 9 + 2) = 11, f(3) = max(11, 3 + 7) = 11.

Code

java
class Solution {
    public int rob(int[] nums) {
        return dfs(nums, nums.length - 1);
    }

    // best loot from houses 0..i
    private int dfs(int[] nums, int i) {
        if (i < 0) {
            return 0;
        }
        int skip = dfs(nums, i - 1);
        int take = nums[i] + dfs(nums, i - 2); // neighbour i - 1 is forbidden
        return Math.max(skip, take);
    }
}

Complexity

  • Time: O(2โฟ) โ€” two branches per house.
  • Space: O(n) โ€” recursion depth.

Complexity Comparison

Approach Time Space
Recursion (Brute Force) O(2โฟ) O(n)
Top-Down Memoization O(n) O(n)
Bottom-Up Tabulation O(n) O(n)
Space-Optimized DP O(n) O(1)

Common Pitfalls

  • Greedily taking all even or all odd indices โ€” [2, 1, 1, 2] needs houses 0 and 3 (answer 4), which neither parity gives.
  • Using 0 as the memo sentinel: houses can hold 0, so many prefix answers are legitimately 0.
  • Indexing nums[1] or dp[2] when n == 1 โ€” handle the single-house case.
  • Updating rob1 before computing newRob โ€” compute the new value first, then shift.