House Robber II

Updated on 02 June, 2025
House Robber II header image

Problem Statement

You are a professional thief who targets a neighborhood where houses are arranged in a circular fashion. In this setup, the first house is directly adjacent to the last house, forming a closed loop. Each house has a certain amount of money stashed, and your challenge is to steal as much as you can. However, there's a catch: the houses have a state-of-the-art security system. If you rob two adjacent houses in one night, the security system will detect the activity and immediately alert the police. Given an integer array nums, which represents the amount of money in each house, your task is to determine the maximum amount of money you can rob without triggering any alarms.

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 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 and Intuition

The problem presents an interesting twist on the classic "house robber" problem by introducing a circular arrangement of houses. Here's how you can think about solving it while maintaining the constraints:

  1. Understanding the problem:

    • If you rob the first house (nums[0]), then you cannot rob the last house (nums[length-1]), as they are adjacent in a circular list.
    • Deciding not to rob the first house means you can consider robbing from the second house to the last.
    • Essentially, the problem can be broken down into two separate scenarios:
      • Rob houses from index 0 to length-2.
      • Rob houses from index 1 to length-1.
  2. Approach to solve:

    • Utilize dynamic programming to solve for the maximum amount robbable from a linear set of houses.
    • Define a function that calculates the maximum rob-able amount for a linear list of houses.
    • Use this function on the two scenarios described:
      • From the first house to the second-last.
      • From the second house to the last.
  3. Implementation steps:

    • Define a helper function, say robLinear(houses), which implements the robbery logic on a linear list of houses. This would typically involve iterating over the houses while maintaining two variables that track the maximum amounts rob-able with and without robbing the current house.
    • Use this helper function twice as per the cases derived:
      • Once for the sublist excluding the last house.
      • Once for the sublist excluding the first house.
    • The final result will be the maximum value obtained from these two function calls.

This approach leverages the technique of breaking down the problem into simpler subproblems (linear vs circular), a hallmark of dynamic programming. This ensures that the solution is both efficient and comprehensive while adhering to the imposed constraint of a circular arrangement.

Solutions

  • C++
  • Java
  • Python
cpp
class Solution {
public:
    int houseRobber(vector<int>& houses) {
        if (houses.size() == 0) return 0;

        if (houses.size() == 1) return houses[0];

        int rob1 = robRange(houses, 0, houses.size() - 2);
        int rob2 = robRange(houses, 1, houses.size() - 1);

        return max(rob1, rob2);
    }

private:
    int robRange(vector<int>& houses, int start, int end) {
        int prev = 0, curr = 0;
        for (int i = start; i <= end; ++i) {
            int temp = curr;
            curr = max(houses[i] + prev, curr);
            prev = temp;
        }

        return curr;
    }
};

This solution addresses the "House Robber II" problem, which extends the basic house robber problem by dealing with houses arranged in a circle, meaning the first and last house are adjacent. The approach implemented in C++ involves dynamic programming, where the main challenge is to avoid the circular condition (first house is adjacent to the last).

Given a class Solution with two primary functions:

  • houseRobber(vector<int>& houses): Determines the maximum amount of money that can be robbed. Handles special cases where the number of houses is zero (returns 0) or one (returns the value of the only house). The function solves the problem by considering two scenarios:

    1. Robbing houses from the first to the second-last (ignoring the last house due to circular arrangement).
    2. Robbing houses from the second to the last (ignoring the first house).

    It then returns the maximum of the two scenarios using the helper function robRange.

  • robRange(vector<int>& houses, int start, int end): A helper function that implements the dynamic programming part of the solution for a specified range of houses. It iterates through the houses, deciding at each step whether to rob the current house or skip it based on the maximum robberies calculated from previous houses.

This function uses two variables, prev and curr, to store the maximum robbery amounts up to the previous house and the current house, respectively, thereby avoiding the use of extra memory for dp array.

The approach in this solution efficiently handles the circular house arrangement by breaking it down into two linear subproblems and is optimal with a time complexity of O(n), where n is the number of houses. The use of iterative dynamic programming with constant space makes it space-efficient as well.

java
class Solution {
    public int robHouses(int[] houses) {
        if (houses.length == 0) return 0;
        if (houses.length == 1) return houses[0];

        int maxOption1 = computeMax(houses, 0, houses.length - 2);
        int maxOption2 = computeMax(houses, 1, houses.length - 1);

        return Math.max(maxOption1, maxOption2);
    }

    public int computeMax(int[] houses, int start, int finish) {
        int prevMax = 0;
        int beforePrevMax = 0;

        for (int i = start; i <= finish; i++) {
            int currentHouse = houses[i];
            int temp = prevMax;

            prevMax = Math.max(beforePrevMax + currentHouse, prevMax);
            beforePrevMax = temp;
        }

        return prevMax;
    }
}

The provided Java code addresses the problem of robbing houses arranged in a circle, ensuring that the robber does not rob two adjacent houses or the first and last house together, which are considered adjacent due to the circular arrangement. This is a variation of the House Robber problem where the houses are placed in a straight line.

  • The robHouses method first checks if there are no houses or only one house. If there are no houses, it returns 0 as there's nothing to rob. If there's only one house, it returns the value of that house since there's no adjacency issue with a single house.
  • The method then calculates the maximum amount that can be robbed in two scenarios:
    • Ignoring the last house (computeMax from the first house to the second-to-last house).
    • Ignoring the first house (computeMax from the second house to the last house).
  • It uses a helper method, computeMax, to compute the maximum money that can be robbed for a given range of houses. This method uses dynamic programming where prevMax stores the maximum robbed amount considering the house before the current one, and beforePrevMax stores the maximum amount considering two houses before the current one.
  • For each house in the specified range (start to finish), the maximum robbed amount up to that house is updated. The expression Math.max(beforePrevMax + currentHouse, prevMax) ensures that the program does not select two adjacent houses.
  • The final result returned by robHouses is the maximum value obtained between the two computed scenarios using Math.max(maxOption1, maxOption2).

This approach ensures optimal use of resources, leveraging the characteristics of dynamic programming to efficiently solve the problem without violating the neighborhood constraints set by the problem statement.

python
class Solution:
    def rob_homes(self, houses: List[int]) -> int:
        if not houses or len(houses) == 0:
            return 0

        if len(houses) == 1:
            return houses[0]

        return max(self.simple_rob(houses[:-1]), self.simple_rob(houses[1:]))

    def simple_rob(self, houses: List[int]) -> int:
        last = 0
        last_but_one = 0
        for amount in houses:
            current = last
            last = max(amount + last_but_one, last)
            last_but_one = current
        return last

This summary outlines a Python solution for the problem where you aim to determine the maximum amount of money that can be robbed from a row of houses, considering the first and last houses are adjacent (forming a circle). The provided Python class, named Solution, includes methods that implement a dynamic programming approach to solve the problem effectively.

In the rob_homes method:

  • Initially, handle edge cases where the list of houses is empty or contains only one house.
  • If there's only one house, return the value of that house.
  • For multiple houses, the problem is broken down into two scenarios due to the circular arrangement:
    • Robbing the houses from the start to the second-to-last.
    • Robbing the houses from the second house to the last.

The choice between these two scenarios is determined by which offers a greater total return, obtained using the max function.

The simple_rob method, used in both scenarios, employs the dynamic programming technique for a linear sequence of houses:

  • Initialize two trackers, last and last_but_one, to manage the maximum amounts that can be robbed up to the current house without triggering alarms.
  • Loop through each amount in the list of houses. At each step, compute the maximum money that can be robbed either by including the current house or skipping it.
  • Update the trackers accordingly.

In essence, with this solution, you effectively account for the circular arrangement by considering both linear sub-problems, ensuring optimal theft strategy without alerting the house security systems.

Comments

No comments yet.