Source: https://leetcode.com/problems/house-robber/

You are a professional robber planning to rob houses along a street. Each house has a certain amount of money stashed, the only constraint stopping you from robbing each of them is that adjacent houses have security systems 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.

/**
* @param {number[]} nums
* @return {number}
*/
/*
Categorize: Knapsack 0/1, Recursion/Backtracking
States: Keep track of the currentIndex (starting at 0) for the house and currentSum (starting at 0 and will be computed along the way)
Decisions: Whether to rob the current house or skip the current house and rob the next house
Base Case: After going through all the houses (currentIndex >= nums.length) return 0; otherwise, keep computing the max of robbing the current house or robbing the next house
Implement: Recursion
Optimize: Memoize max value given currentIndex
We make 2 choices at each step so it's O(2^n) time complexity; may stack overflow due to recursion unless we optimize with memoization
Memoization cuts it down to O(n) time, O(n) function space
*/
var rob = function(nums) {
function recRob(currentIndex) {
// If we've already computed this subproblem before, we will use that memoized value
if (memo.hasOwnProperty(currentIndex)) {
return memo[currentIndex];
}
// If we've gone through all the houses, return 0
if (currentIndex >= nums.length) {
return 0;
}
// Assuming we haven't gone through all the houses, recursively go through these steps
// Compute max from the 2 choices: robbing the current house or robbing the next house
const currentNum = nums[currentIndex];
const max = Math.max(recRob(currentIndex + 2) + currentNum, recRob(currentIndex + 1));
// Store max in memo to avoid re-computing the same problem
memo[currentIndex] = max;
return max;
}
const memo = {};
const maxRobbed = recRob(0);
return maxRobbed;
};

Source: https://leetcode.com/problems/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.

/**
* @param {number[]} nums
* @return {number}
*/
// Can reuse logic from House Robber I
// Dynamic Programming: Keeping track of two variables i.e. rob1 (2 houses back), rob2 (1 house back) aka max robbed so far at a certain house
// Initialize rob1 and rob2 with 0
// Loop through each house value
// Update rob2 with max of adding rob1 (2 houses back) + currentRob (current house) or skipping the current house (rob2)
// Update rob1 with last rob2
// For House Robber II we can reuse the House Robber I logic in a helper function and call it twice
// We will take the max of skipping the first house or skipping the last house or the first house in the case we have only one house to go through
// O(N) time
var rob = function(nums) {
function robHelper(currentNums) {
let rob1 = 0;
let rob2 = 0;
currentNums.forEach((currentNum) => {
// Take the max of robbing the prior house and the current house or skipping the current house
const newRob2 = Math.max(rob1 + currentNum, rob2);
rob1 = rob2;
rob2 = newRob2;
});
return rob2;
}
if (nums.length === 1) {
return nums[0];
}
// Since the houses are in a circle, we take the max of skipping the first house or skipping the last house
return Math.max(robHelper(nums.slice(1)), robHelper(nums.slice(0, nums.length - 1)));
};