House Robber II
Medium · 1-D Dynamic Programming · Explanation and code example
Dynamic Programming (Space Optimized)
How it works
This is House Robber II, where houses are arranged in a circle.
Because of the circular setup, you cannot rob both the first and last house.
To handle this, split the problem into two linear subproblems:
- Rob houses excluding the first house.
- Rob houses excluding the last house.
Each subproblem becomes the classic House Robber I, which can be solved using two variables instead of a full DP array.
Steps
- If there is only one house, return its value.
- Compute the maximum money for:
- Houses
nums[1:](skip first) - Houses
nums[:-1](skip last)
- Houses
- For each linear list:
- Maintain two variables:
rob1-> best up to housei-2rob2-> best up to housei-1
- For each house:
- Maintain two variables:
Complexity
- Time complexity:
O(n) - Space complexity:
O(1)
Things to watch for
Forgetting the Single House Edge Case
When there is only one house, the circular constraint does not apply since there is no adjacency conflict. If you split the problem into nums[1:] and nums[:-1] without handling this case, both subarrays become empty when n=1, and the function returns 0 instead of nums[0]. Always check if the array has only one element and return its value directly.
Incorrectly Handling the Circular Constraint
The key insight is that the first and last houses cannot both be robbed. A common mistake is to add complex flag logic throughout the recursion when a simpler approach exists: run the standard House Robber algorithm twice, once excluding the first house and once excluding the last house. Taking the maximum of these two results correctly handles the circular dependency without overcomplicating the code.
Off-by-One Errors in Array Slicing
When splitting the array into nums[1:] (exclude first) and nums[:-1] (exclude last), be careful with language-specific slicing syntax. In some languages, you need to manually copy subarrays or adjust loop bounds. An off-by-one error here can lead to including both the first and last houses in one subproblem, or excluding more houses than intended, producing incorrect results.
Python code example
from typing import List
class Solution:
def rob(self, nums: List[int]) -> int:
return max(nums[0], self.helper(nums[1:]),
self.helper(nums[:-1]))
def helper(self, nums):
rob1, rob2 = 0, 0
for num in nums:
newRob = max(rob1 + num, rob2)
rob1 = rob2
rob2 = newRob
return rob2
C++ code example
class Solution {
public:
int rob(vector<int>& nums) {
vector<int> nums1(nums.begin() + 1, nums.end());
vector<int> nums2(nums.begin(), nums.end() - 1);
return max(nums[0],
max(helper(nums1), helper(nums2)));
}
private:
int helper(vector<int>& nums) {
int rob1 = 0, rob2 = 0;
for (int num : nums) {
int newRob = max(rob1 + num, rob2);
rob1 = rob2;
rob2 = newRob;
}
return rob2;
}
};
Source and license
Adapted from NeetCode’s source · MIT license. Node types are supplied by LeetCode where needed.