Talentd
FleetCode
Make and follow sheets.Sign in

House Robber II solution and explanation

MediumArrayDynamic Programming5 min read45.3% acceptanceUpdated 9 Apr 2026
House Robber II - Dynamic Programming - Leetcode 213NeetCode · 10:40 · 197K viewsWatch on YouTube
9 more videos on this problem
  1. House Robber II - Dynamic Programming - Leetcode 213NeetCode · 10:40 · 197K views
  2. House Robber II | Recur + Memo | DP Concepts & Qns - 5 | Leetcode-213codestorywithMIK · 15:03 · 48K views
  3. House Robber 2 (LeetCode 213) | Full solution with easy explanation | Diagrams | Study AlgorithmsNikhil Lohia · 10:01 · 24K views
  4. House Robber II | Live Coding with Explanation | Leetcode - 213Algorithms Made Easy · 6:10 · 6K views
  5. Leetcode - House Robber II (Python)Timothy H Chang · 5:33 · 2K views
  6. HOUSE ROBBER II | LEETCODE 213 | PYTHON SOLUTIONCracking FAANG · 8:36 · 1K views
  7. Apple Coding Interview Question - House Robber 2 - LeetCode 213AlgoJS · 6:12 · 1K views
  8. House Robber II | LeetCode 213 | October LeetCode Challenge | Day 14Learn Code Repeat · 9:41 · 691 views
  9. House Robber II Leetcode 213 video | leetcode solutionEasyLeetcode · 9:28 · 474 views
  10. DP 6. House Robber 2 | 1D DP | DP on Subsequencestake U forward · 9:50 · 521K views

The problem

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.

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 houses.

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

Constraints:

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

Approach overview

Problem Overview: You are given an array where each element represents money stored in a house. Houses are arranged in a circle, so the first and last houses are neighbors. You cannot rob two adjacent houses. The task is to compute the maximum amount you can rob without triggering alarms.

The circular layout is the only difference from the classic House Robber problem. If you rob the first house, you cannot rob the last. If you rob the last house, you cannot rob the first. This constraint splits the problem into two independent linear subproblems.

Approach 1: Optimization with Memoization (Time: O(n), Space: O(n))

This approach uses dynamic programming with recursion and memoization. The key idea is the same as the original House Robber problem: for each index, decide whether to rob the current house or skip it. If you rob it, you add its value and move to i + 2; if you skip it, move to i + 1. Because the houses form a circle, you evaluate two scenarios: robbing within range [0, n-2] and robbing within range [1, n-1]. Each recursive state is cached to avoid recomputation, giving linear time complexity. This version is useful when you want a top‑down perspective that clearly expresses the decision tree.

Approach 2: Dynamic Programming with Two Passes (Time: O(n), Space: O(1))

This is the optimal iterative solution. Instead of recursion, compute the best robbery plan using two passes over the array. First pass assumes you rob houses from 0 to n-2 (excluding the last). Second pass assumes you rob houses from 1 to n-1 (excluding the first). Each pass runs the classic House Robber DP where you track two values: prev1 (max profit up to previous house) and prev2 (max profit up to the house before that). For every element, compute max(prev1, prev2 + nums[i]). Because only two variables are needed, space remains constant.

The insight is that the circular dependency disappears once you split the problem into these two linear ranges. Each pass solves a standard non‑adjacent selection problem using DP transitions.

Recommended for interviews: The dynamic programming two‑pass approach is what most interviewers expect. It runs in O(n) time with O(1) extra space and clearly demonstrates understanding of circular constraints. The memoization version still shows strong dynamic programming fundamentals and is often easier to reason about initially, but the iterative optimization shows stronger mastery.

Approach 1: Dynamic Programming with Two Passes

This approach involves splitting the problem into two scenarios: robbing from house 0 to house n-2, and from house 1 to house n-1. The aim is to apply the linear version of the House Robber problem to each scenario separately and then take the maximum of the two resulting summed values.


def robLinear(nums):
    prev1, prev2 = 0, 0
    for num in nums:
        temp = prev1
        prev1 = max(num + prev2, prev1)
        prev2 = temp
    return prev1

class Solution:
    def rob(self, nums):
        if len(nums) == 1:
            return nums[0]
        return max(robLinear(nums[:-1]), robLinear(nums[1:]))

class Solution {
    private int robLinear(int[] nums, int start, int end) {
        int prev1 = 0, prev2 = 0;
        for (int i = start; i < end; i++) {
            int temp = prev1;
            prev1 = Math.max(nums[i] + prev2, prev1);
            prev2 = temp;
        }
        return prev1;
    }

    public int rob(int[] nums) {
        int n = nums.length;
        if (n == 1) return nums[0];
        return Math.max(robLinear(nums, 0, n - 1), robLinear(nums, 1, n));
    }
}

class Solution {
public:
    int robLinear(vector<int>& nums, int start, int end) {
        int prev1 = 0, prev2 = 0;
        for (int i = start; i < end; i++) {
            int temp = prev1;
            prev1 = max(nums[i] + prev2, prev1);
            prev2 = temp;
        }
        return prev1;
    }
    int rob(vector<int>& nums) {
        int n = nums.size();
        if (n == 1) return nums[0];
        return max(robLinear(nums, 0, n - 1), robLinear(nums, 1, n));
    }
};

var robLinear = function(nums) {
    let prev1 = 0, prev2 = 0;
    for (const num of nums) {
        let temp = prev1;
        prev1 = Math.max(num + prev2, prev1);
        prev2 = temp;
    }
    return prev1;
};

var rob = function(nums) {
    if (nums.length === 1) return nums[0];
    return Math.max(robLinear(nums.slice(0, nums.length - 1)), robLinear(nums.slice(1)));
};

int robLinear(int* nums, int len) {
    if (len == 0) return 0;
    if (len == 1) return nums[0];
    int prev1 = 0, prev2 = 0;
    for (int i = 0; i < len; i++) {
        int temp = prev1;
        prev1 = (nums[i] + prev2 > prev1) ? nums[i] + prev2 : prev1;
        prev2 = temp;
    }
    return prev1;
}

int rob(int* nums, int numsSize) {
    if (numsSize == 1) return nums[0];
    return (robLinear(nums, numsSize - 1) > robLinear(nums + 1, numsSize - 1)) 
            ? robLinear(nums, numsSize - 1) 
            : robLinear(nums + 1, numsSize - 1);
}

public class Solution {
    private int RobLinear(int[] nums, int start, int end) {
        int prev1 = 0, prev2 = 0;
        for (int i = start; i < end; i++) {
            int temp = prev1;
            prev1 = Math.Max(nums[i] + prev2, prev1);
            prev2 = temp;
        }
        return prev1;
    }

    public int Rob(int[] nums) {
        if (nums.Length == 1) return nums[0];
        return Math.Max(RobLinear(nums, 0, nums.Length - 1), RobLinear(nums, 1, nums.Length));
    }
}

This Python solution utilizes a helper function robLinear to handle the linear robbery problem. It then applies this helper to the range excluding first and last houses respectively, returning the greatest result.

Complexity

Time Complexity: O(n).
Space Complexity: O(1).

Try this approach in the editor

Approach 2: Optimization with Memoization

This approach implements a memoized version of the dynamic programming solution to avoid recomputing values. We explore two scenarios of the circle, taking into account the cycles explicitly by caching intermediate results.


class Solution:
    def robHelper(self, nums, start, end, memo):
        if start >= end:
            return 0
        if memo[start] >= 0:
            return memo[start]
        memo[start] = max(nums[start] + self.robHelper(nums, start + 2, end, memo), self.robHelper(nums, start + 1, end, memo))
        return memo[start]

    def rob(self, nums):
        if len(nums) == 1:
            return nums[0]
        memo1 = [-1] * len(nums)
        memo2 = [-1] * len(nums)
        return max(self.robHelper(nums, 0, len(nums) - 1, memo1), self.robHelper(nums, 1, len(nums), memo2))

class Solution {
    private int[] memo;
    private int robHelper(int[] nums, int start, int end) {
        if (start >= end) return 0;
        if (memo[start] >= 0) return memo[start];
        memo[start] = Math.max(nums[start] + robHelper(nums, start + 2, end), robHelper(nums, start + 1, end));
        return memo[start];
    }

    public int rob(int[] nums) {
        if (nums.length == 1) return nums[0];
        memo = new int[nums.length];
        Arrays.fill(memo, -1);
        int case1 = robHelper(nums, 0, nums.length - 1);
        Arrays.fill(memo, -1);
        int case2 = robHelper(nums, 1, nums.length);
        return Math.max(case1, case2);
    }
}

class Solution {
    vector<int> memo;
    int robHelper(vector<int>& nums, int start, int end) {
        if (start >= end) return 0;
        if (memo[start] != -1) return memo[start];
        memo[start] = max(nums[start] + robHelper(nums, start + 2, end), robHelper(nums, start + 1, end));
        return memo[start];
    }

public:
    int rob(vector<int>& nums) {
        if (nums.size() == 1) return nums[0];
        memo.assign(nums.size(), -1);
        int case1 = robHelper(nums, 0, nums.size() - 1);
        memo.assign(nums.size(), -1);
        int case2 = robHelper(nums, 1, nums.size());
        return max(case1, case2);
    }
};

var robHelper = function(nums, start, end, memo) {
    if (start >= end) return 0;
    if (memo[start] !== undefined) return memo[start];
    memo[start] = Math.max(nums[start] + robHelper(nums, start + 2, end, memo), robHelper(nums, start + 1, end, memo));
    return memo[start];
};

var rob = function(nums) {
    if (nums.length === 1) return nums[0];
    const memo1 = {};
    const memo2 = {};
    return Math.max(robHelper(nums, 0, nums.length - 1, memo1), robHelper(nums, 1, nums.length, memo2));
};

#include <string.h>

int memo[101];

int robLinearMemo(int* nums, int start, int end) {
    memset(memo, -1, sizeof(memo));
    return robHelper(nums, start, end);
}

int robHelper(int* nums, int start, int end) {
    if (start >= end) return 0;
    if (memo[start] >= 0) return memo[start];
    memo[start] = (nums[start] + robHelper(nums, start + 2, end) > robHelper(nums, start + 1, end)) 
                  ? nums[start] + robHelper(nums, start + 2, end) : robHelper(nums, start + 1, end);
    return memo[start];
}

int rob(int* nums, int numsSize) {
    if (numsSize == 1) return nums[0];
    int case1 = robLinearMemo(nums, 0, numsSize - 1);
    int case2 = robLinearMemo(nums, 1, numsSize);
    return (case1 > case2) ? case1 : case2;
}

public class Solution {
    private Dictionary<int, int> memo = new Dictionary<int, int>();
    private int RobHelper(int[] nums, int start, int end) {
        if (start >= end) return 0;
        if (memo.ContainsKey(start)) return memo[start];
        int result = Math.Max(nums[start] + RobHelper(nums, start + 2, end), RobHelper(nums, start + 1, end));
        memo[start] = result;
        return result;
    }

    public int Rob(int[] nums) {
        if (nums.Length == 1) return nums[0];
        memo.Clear();
        int case1 = RobHelper(nums, 0, nums.Length - 1);
        memo.Clear();
        int case2 = RobHelper(nums, 1, nums.Length);
        return Math.Max(case1, case2);
    }
}

This Python solution makes use of a memo array to store intermediate maximums to prevent redundant calculations. The robHelper function recursively determines possible rob outcomes and updates the cache, providing two results depending on whether the first house is considered or not.

Complexity

Time Complexity: O(n).
Space Complexity: O(n) allocated for memoization.

Try this approach in the editor

Approach 3: Dynamic Programming

The circular arrangement means that at most one of the first and last houses can be chosen for theft, so this circular arrangement problem can be reduced to two single-row house problems.

The time complexity is O(n), where n is the length of the array. The space complexity is O(1).

function rob(nums: number[]): number {
    const n = nums.length;
    if (n === 1) {
        return nums[0];
    }
    const robRange = (l: number, r: number): number => {
        let [f, g] = [0, 0];
        for (; l <= r; ++l) {
            [f, g] = [Math.max(f, g), f + nums[l]];
        }
        return Math.max(f, g);
    };
    return Math.max(robRange(0, n - 2), robRange(1, n - 1));
}
func rob(nums []int) int {
	n := len(nums)
	if n == 1 {
		return nums[0]
	}
	return max(robRange(nums, 0, n-2), robRange(nums, 1, n-1))
}

func robRange(nums []int, l, r int) int {
	f, g := 0, 0
	for _, x := range nums[l : r+1] {
		f, g = max(f, g), f+x
	}
	return max(f, g)
}
impl Solution {
    pub fn rob(nums: Vec<i32>) -> i32 {
        let n = nums.len();
        if n == 1 {
            return nums[0];
        }
        let rob_range = |l, r| {
            let mut f = [0, 0];
            for i in l..r {
                f = [f[0].max(f[1]), f[0] + nums[i]];
            }
            f[0].max(f[1])
        };
        rob_range(0, n - 1).max(rob_range(1, n))
    }
}
Try this approach in the editor

Complexity comparison

ApproachComplexity
Dynamic Programming with Two Passes

Time Complexity: O(n).
Space Complexity: O(1).

Optimization with Memoization

Time Complexity: O(n).
Space Complexity: O(n) allocated for memoization.

Dynamic Programming–

Complexity analysis

ApproachTimeSpaceWhen to Use
Memoization (Top-Down DP)O(n)O(n)When recursion is easier to reason about and you want explicit subproblem caching.
Dynamic Programming with Two PassesO(n)O(1)Best practical solution. Handles circular constraint by solving two linear DP cases.

Frequently asked questions

Is House Robber II easy or hard?

House Robber II is rated Medium difficulty on LeetCode. The dynamic programming transition is straightforward, but recognizing how to handle the circular arrangement of houses is the main challenge.

House Robber II Python/Java solution

Python and Java implementations both follow the same DP logic: compute the best robbery value for ranges [0..n-2] and [1..n-1]. Each iteration updates two variables representing previous optimal values. This keeps the solution O(n) time and O(1) space.

How to solve House Robber II in O(n)?

Split the circular problem into two linear robbery problems: exclude the last house or exclude the first house. Run the classic House Robber dynamic programming transition max(prev1, prev2 + nums[i]) for each range. Compute both results and return the larger value.

What is the best approach for House Robber II?

The best approach is dynamic programming with two passes. Because the houses form a circle, you cannot rob both the first and last house. Solve two linear cases: houses [0..n-2] and houses [1..n-1], then take the maximum result. This runs in O(n) time and O(1) space.

Is House Robber II asked at Google/Amazon/Meta?

House Robber variants frequently appear in interviews at companies like Amazon, Google, and Meta because they test dynamic programming fundamentals. Interviewers expect candidates to recognize the circular constraint and reduce it to two linear DP problems.

What data structure is used in House Robber II?

The solution primarily uses arrays and dynamic programming state variables. In the optimized version, only two variables are needed to track the previous DP states, reducing space complexity to constant space.

What is the time complexity of House Robber II?

The optimal solution runs in O(n) time because each house is processed a constant number of times. Space complexity can be reduced to O(1) by storing only the last two DP states instead of a full array.

Comments

Now write it yourselfClose this page and solve House Robber II in the editor, without looking.
Practise House Robber II