213. House Robber II
At a Glance
- Topic: dp-1d
- Pattern: Dynamic Programming (circular array reduction)
- Difficulty: Medium
- Companies: Google, Amazon, Bloomberg, Microsoft, Apple
- Frequency: High
- LeetCode: 213
Problem (one-liner)
Same as House Robber, but houses are arranged in a circle: first and last houses are adjacent; still no two adjacent robberies. Input: nums circular. Output: max sum.
Recognition Cues
- “Circle” / “first house is neighbor of last”
- Otherwise identical to linear robber
- Reduce to two linear subproblems
Diagram
At-a-glance flow (replace with problem-specific Mermaid as you refine this note). camelCase node IDs; no spaces in IDs.
Loading diagram…
Approaches
- Brute force — try subsets respecting circular adjacency — exponential.
- Better — DP on broken circle — run linear robber twice on ranges
[0..n-2]and[1..n-1]. - Optimal —
O(n)time /O(1)space per range — take max of two linear passes. <- pick this in interview.
Optimal Solution
Go
package main
func robLinear(nums []int, start int, endExclusive int) int {
if start >= endExclusive {
return 0
}
if endExclusive-start == 1 {
return nums[start]
}
previousPrevious := nums[start]
previous := nums[start]
if nums[start+1] > nums[start] {
previous = nums[start+1]
}
for index := start + 2; index < endExclusive; index++ {
take := previousPrevious + nums[index]
skip := previous
next := skip
if take > skip {
next = take
}
previousPrevious = previous
previous = next
}
return previous
}
func robTable(nums []int) int {
length := len(nums)
if length == 0 {
return 0
}
if length == 1 {
return nums[0]
}
excludingLast := robLinear(nums, 0, length-1)
excludingFirst := robLinear(nums, 1, length)
if excludingLast > excludingFirst {
return excludingLast
}
return excludingFirst
}
func rob(nums []int) int {
return robTable(nums)
}JavaScript
function robLinear(nums, start, endExclusive) {
if (start >= endExclusive) return 0;
if (endExclusive - start === 1) return nums[start];
let previousPrevious = nums[start];
let previous = Math.max(nums[start], nums[start + 1]);
for (let index = start + 2; index < endExclusive; index++) {
const next = Math.max(previous, previousPrevious + nums[index]);
previousPrevious = previous;
previous = next;
}
return previous;
}
function rob(nums) {
const length = nums.length;
if (length === 0) return 0;
if (length === 1) return nums[0];
return Math.max(
robLinear(nums, 0, length - 1),
robLinear(nums, 1, length)
);
}Walkthrough
Input: nums = [2, 3, 2]
- Range
[0..1](drop last): linear[2,3]→ best3. - Range
[1..2](drop first): linear[3,2]→ best3. - Answer
max(3,3)=3.
Invariant: any valid circular solution either skips the first house or skips the last; those sets partition all feasible solutions.
Edge Cases
n == 1: only one housen == 2: take max of the two- All equal values
Pitfalls
- Running linear robber on full array (double-counts first+last)
- Empty inclusive/exclusive slice bounds in helper
- Duplicating code instead of reusing
roblinear helper
Similar Problems
- 198. House Robber — linear base case.
- 070. Climbing Stairs — another linear two-step recurrence pattern.
- 300. Longest Increasing Subsequence — different objective, same “prefix DP” thinking.
Variants
- “Rob at most
khouses” on a cycle → harder constrained DP.
Mind-Map Tags
#dp-1d #circular-array #house-robber #two-pass #medium
Last updated on
Spotted something unclear or wrong on this page?