💡 Memoization caches results of recursive calls to avoid recomputation, drastically improving efficiency.
Intuition
Store results of subproblems so that each is computed once, turning exponential recursion into linear time.
Algorithm
- Create a memo dictionary or array to store results of subproblems.
- Modify the recursive function to check memo before computing.
- Compute max loot for two linear cases excluding first or last house.
- Return the maximum of the two cases.
💡 Memoization transforms the brute force recursion into a dynamic programming solution by caching.
Recurrence:rob(i) = max(rob(i-1), rob(i-2) + nums[i])
def rob_linear(nums, start, end, memo):
if start > end:
return 0
if start == end:
return nums[start]
if start in memo:
return memo[start]
memo[start] = max(rob_linear(nums, start + 1, end, memo), rob_linear(nums, start + 2, end, memo) + nums[start])
return memo[start]
def rob(nums):
n = len(nums)
if n == 1:
return nums[0]
return max(rob_linear(nums, 0, n - 2, {}), rob_linear(nums, 1, n - 1, {}))
# Example usage
if __name__ == '__main__':
print(rob([2,3,2])) # Output: 3
Line Notes
def rob_linear(nums, start, end, memo):Add memo dict to cache results
if start in memo:Return cached result if available
memo[start] = max(rob_linear(nums, start + 1, end, memo), rob_linear(nums, start + 2, end, memo) + nums[start])Store computed max loot for current start
return max(rob_linear(nums, 0, n - 2, {}), rob_linear(nums, 1, n - 1, {}))Compute two linear cases with memoization
import java.util.HashMap;
public class Solution {
public int robLinear(int[] nums, int start, int end, HashMap<Integer, Integer> memo) {
if (start > end) return 0;
if (start == end) return nums[start];
if (memo.containsKey(start)) return memo.get(start);
int result = Math.max(robLinear(nums, start + 1, end, memo), robLinear(nums, start + 2, end, memo) + nums[start]);
memo.put(start, result);
return result;
}
public int rob(int[] nums) {
int n = nums.length;
if (n == 1) return nums[0];
return Math.max(robLinear(nums, 0, n - 2, new HashMap<>()), robLinear(nums, 1, n - 1, new HashMap<>()));
}
public static void main(String[] args) {
Solution sol = new Solution();
System.out.println(sol.rob(new int[]{2,3,2})); // Output: 3
}
}
Line Notes
HashMap<Integer, Integer> memoMemo map caches results for subproblems
if (memo.containsKey(start)) return memo.get(start);Return cached result if present
memo.put(start, result);Store computed result in memo
return Math.max(robLinear(nums, 0, n - 2, new HashMap<>()), robLinear(nums, 1, n - 1, new HashMap<>()));Compute two linear cases with memoization
#include <iostream>
#include <vector>
#include <unordered_map>
#include <algorithm>
using namespace std;
int robLinear(const vector<int>& nums, int start, int end, unordered_map<int,int>& memo) {
if (start > end) return 0;
if (start == end) return nums[start];
if (memo.count(start)) return memo[start];
memo[start] = max(robLinear(nums, start + 1, end, memo), robLinear(nums, start + 2, end, memo) + nums[start]);
return memo[start];
}
int rob(const vector<int>& nums) {
int n = nums.size();
if (n == 1) return nums[0];
unordered_map<int,int> memo1, memo2;
return max(robLinear(nums, 0, n - 2, memo1), robLinear(nums, 1, n - 1, memo2));
}
int main() {
vector<int> nums = {2,3,2};
cout << rob(nums) << endl; // Output: 3
return 0;
}
Line Notes
unordered_map<int,int>& memoMemo map caches subproblem results
if (memo.count(start)) return memo[start];Return cached result if exists
memo[start] = max(robLinear(nums, start + 1, end, memo), robLinear(nums, start + 2, end, memo) + nums[start]);Store computed max loot
return max(robLinear(nums, 0, n - 2, memo1), robLinear(nums, 1, n - 1, memo2));Compute two linear cases with memoization
function robLinear(nums, start, end, memo) {
if (start > end) return 0;
if (start === end) return nums[start];
if (memo.has(start)) return memo.get(start);
const result = Math.max(robLinear(nums, start + 1, end, memo), robLinear(nums, start + 2, end, memo) + nums[start]);
memo.set(start, result);
return result;
}
function rob(nums) {
const n = nums.length;
if (n === 1) return nums[0];
return Math.max(robLinear(nums, 0, n - 2, new Map()), robLinear(nums, 1, n - 1, new Map()));
}
// Example usage
console.log(rob([2,3,2])); // Output: 3
Line Notes
function robLinear(nums, start, end, memo)Memo map caches results
if (memo.has(start)) return memo.get(start);Return cached result if available
memo.set(start, result);Store computed result in memo
return Math.max(robLinear(nums, 0, n - 2, new Map()), robLinear(nums, 1, n - 1, new Map()));Compute two linear cases with memoization
TimeO(n)
SpaceO(n) for memo and recursion stack
Each subproblem is solved once and cached, reducing exponential calls to linear.
💡 For n=10^5, this approach is efficient enough to run within time limits.
Interview Verdict: Accepted
Memoization is a common optimization that turns brute force into a practical solution.