[Medium] 213. House Robber II
You are a professional robber planning to rob houses along a street. Each house has a certain amount of money stashed. All houses at this place are arranged in a circle. That means the first house is the neighbor of the last one. Meanwhile, adjacent houses have a security system connected, and it will automatically contact the police if two adjacent houses were broken into on the same night.
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.
Examples
Example 1:
Input: nums = [2,3,2]
Output: 3
Explanation: You cannot rob house 1 (money = 2) and then rob house 3 (money = 2), because they are adjacent.
Example 2:
Input: nums = [1,2,3,1]
Output: 4
Explanation: Rob house 1 (money = 1) and then rob house 3 (money = 3).
Total amount you can rob = 1 + 3 = 4.
Example 3:
Input: nums = [1,2,3]
Output: 3
Explanation: Rob house 2 (money = 2) or house 3 (money = 3).
Constraints
1 <= nums.length <= 1000 <= nums[i] <= 1000
Thinking Process
- Circular Constraint: First and last houses are adjacent, so we can’t rob both
- Exclude last house:
[0..N-2] - Exclude first house:
[1..N-1]
- Exclude last house:
- Define state: what subproblem does
dp[i](ordp[i][j]) represent? - Recurrence: how does the answer build from smaller indices?
- Base cases first; optimize space if only prior row/layer is needed.
Common Approaches
Typical techniques for this pattern:
| Approach | Time | Space | Notes |
|---|---|---|---|
| 1D DP (this problem) | O(n) | O(n) or O(1) | Linear recurrence |
| 2D DP | O(nm) | O(nm) or O(n) | Grid or two-sequence problems |
| State machine DP | O(n) | O(1) | Buy/sell, hold/not-hold states |
| Memoization (top-down) | Same as DP | O(n) | Recursive + cache |
Solution
class Solution:
/
Cycle: rtn max(robLinear(0.. N - 2), robLinear(1,.. N - 1))
dp[i] = max(dp[i-2] + nums[i], dp[i - 1])
/
def rob(self, nums):
N = len(nums)
if(N == 1) return nums[0]
return max(
rob(nums, 0, N - 2),
rob(nums, 1, N - 1)
)
def rob(self, nums, l, r):
prev2 = 0, prev1 = 0
for(i = l i <= r i += 1) :
curr = max(prev2 + nums[i], prev1)
prev2 = prev1
prev1 = curr
return prev1
Solution Explanation
Approach: 1D DP (this problem)
Key idea: 1. Circular Constraint: First and last houses are adjacent, so we can’t rob both
How the code works:
- Circular Constraint: First and last houses are adjacent, so we can’t rob both
- Exclude last house:
[0..N-2] - Exclude first house:
[1..N-1] - Define state: what subproblem does
dp[i](ordp[i][j]) represent? - Recurrence: how does the answer build from smaller indices?
- Base cases first; optimize space if only prior row/layer is needed.
- Exclude last house:
Walkthrough — input nums = [2,3,2], expected output 3:
You cannot rob house 1 (money = 2) and then rob house 3 (money = 2), because they are adjacent.
Common Mistakes
- Single house:
nums = [5]→ return5 - Two houses:
nums = [2,3]→ returnmax(2,3) = 3 - Three houses:
nums = [2,3,2]→ return3(can’t rob first and last) - All same:
nums = [1,1,1,1]→ return2(rob two non-adjacent) -
First and last are large:
nums = [10,1,1,10]→ return10(rob either first or last) - Not handling single house: Forgetting edge case
N == 1 - Wrong range: Using
[0..N-1]and[1..N]instead of[0..N-2]and[1..N-1] - Including both endpoints: Trying to include both first and last in one case
- Not taking maximum: Forgetting to compare both cases
- Index out of bounds: Not checking bounds when
N == 1orN == 2
Why This Works
Circular Constraint:
Since houses are arranged in a circle, if we rob house 0, we cannot rob house N-1. This creates two mutually exclusive cases:
- Case 1: Rob houses
[0..N-2]- We can rob house 0, but not house N-1
- This is a linear problem
- Case 2: Rob houses
[1..N-1]- We cannot rob house 0, but can rob house N-1
- This is also a linear problem
By taking the maximum of both cases, we ensure we get the optimal solution while respecting the circular constraint.
Visual Representation:
Case 1: [0..N-2] (exclude last)
[0] [1] [2] ... [N-2] [N-1]
✓ ? ? ... ? ✗
Case 2: [1..N-1] (exclude first)
[0] [1] [2] ... [N-2] [N-1]
✗ ? ? ... ? ✓
Result: max(case1, case2)
Related Problems
- LC 198: House Robber - Linear version
- LC 337: House Robber III - Binary tree structure
- LC 740: Delete and Earn - Similar DP pattern
- LC 256: Paint House - Similar constraint pattern
This problem extends House Robber to a circular arrangement. The key insight is breaking the circular constraint into two linear subproblems and taking the maximum, effectively reducing a circular problem to two linear problems.
Key Takeaways
- Circular Constraint: First and last houses are adjacent, so we can’t rob both
- Break into Two Cases:
- Exclude last house:
[0..N-2] - Exclude first house:
[1..N-1]
- Exclude last house:
- Reuse Linear Solution: Use the same DP logic from House Robber
- Space Optimization: O(1) space using two variables instead of array
- Edge Case: Single house doesn’t need splitting
References
- LC 213: House Robber II on LeetCode
- LeetCode Discuss — LC 213: House Robber II
- LeetCode Editorial (may require premium)