💡 Memoization caches results of recursive calls to avoid recomputation, drastically improving efficiency while keeping the recursive intuition.
Intuition
Store results of dfs(i, holding) in a cache so repeated calls return instantly.
Algorithm
- Create a memo dictionary keyed by (day, holding).
- Before recursive call, check if result is cached.
- If cached, return it immediately.
- Otherwise, compute recursively and store in cache.
- Return cached result.
💡 Memoization turns exponential recursion into polynomial time by reusing results.
Recurrence:Same as brute force: f(i, holding) = max(...)
def maxProfit(prices):
n = len(prices)
memo = {}
def dfs(i, holding):
if i == n:
return 0
if (i, holding) in memo:
return memo[(i, holding)]
if holding:
sell = dfs(i+1, False) + prices[i]
hold = dfs(i+1, True)
memo[(i, holding)] = max(sell, hold)
else:
buy = dfs(i+1, True) - prices[i]
skip = dfs(i+1, False)
memo[(i, holding)] = max(buy, skip)
return memo[(i, holding)]
return dfs(0, False)
if __name__ == '__main__':
print(maxProfit([7,1,5,3,6,4])) # 7
Line Notes
memo = {}Initialize cache to store computed results
if (i, holding) in memo:Check if result already computed to avoid recomputation
memo[(i, holding)] = max(sell, hold)Store computed max profit for holding state
memo[(i, holding)] = max(buy, skip)Store computed max profit for not holding state
return memo[(i, holding)]Return cached or newly computed result
import java.util.*;
public class Solution {
private Map<String, Integer> memo = new HashMap<>();
public int maxProfit(int[] prices) {
return dfs(prices, 0, false);
}
private int dfs(int[] prices, int i, boolean holding) {
if (i == prices.length) return 0;
String key = i + "," + holding;
if (memo.containsKey(key)) return memo.get(key);
int result;
if (holding) {
int sell = dfs(prices, i + 1, false) + prices[i];
int hold = dfs(prices, i + 1, true);
result = Math.max(sell, hold);
} else {
int buy = dfs(prices, i + 1, true) - prices[i];
int skip = dfs(prices, i + 1, false);
result = Math.max(buy, skip);
}
memo.put(key, result);
return result;
}
public static void main(String[] args) {
Solution sol = new Solution();
System.out.println(sol.maxProfit(new int[]{7,1,5,3,6,4})); // 7
}
}
Line Notes
private Map<String, Integer> memo = new HashMap<>();Cache results keyed by day and holding state
String key = i + "," + holding;Create unique key for memoization
if (memo.containsKey(key)) return memo.get(key);Return cached result if available
memo.put(key, result);Store computed result in cache
return result;Return computed or cached result
#include <iostream>
#include <vector>
#include <unordered_map>
using namespace std;
class Solution {
unordered_map<string, int> memo;
public:
int maxProfit(vector<int>& prices) {
return dfs(prices, 0, false);
}
private:
int dfs(vector<int>& prices, int i, bool holding) {
if (i == prices.size()) return 0;
string key = to_string(i) + "," + (holding ? "1" : "0");
if (memo.count(key)) return memo[key];
int result;
if (holding) {
int sell = dfs(prices, i + 1, false) + prices[i];
int hold = dfs(prices, i + 1, true);
result = max(sell, hold);
} else {
int buy = dfs(prices, i + 1, true) - prices[i];
int skip = dfs(prices, i + 1, false);
result = max(buy, skip);
}
memo[key] = result;
return result;
}
};
int main() {
Solution sol;
vector<int> prices = {7,1,5,3,6,4};
cout << sol.maxProfit(prices) << endl; // 7
return 0;
}
Line Notes
unordered_map<string, int> memo;Cache results with string keys
string key = to_string(i) + "," + (holding ? "1" : "0");Create unique key for memo
if (memo.count(key)) return memo[key];Return cached result if exists
memo[key] = result;Store computed result
return result;Return result
function maxProfit(prices) {
const n = prices.length;
const memo = new Map();
function dfs(i, holding) {
if (i === n) return 0;
const key = i + ',' + holding;
if (memo.has(key)) return memo.get(key);
let result;
if (holding) {
const sell = dfs(i + 1, false) + prices[i];
const hold = dfs(i + 1, true);
result = Math.max(sell, hold);
} else {
const buy = dfs(i + 1, true) - prices[i];
const skip = dfs(i + 1, false);
result = Math.max(buy, skip);
}
memo.set(key, result);
return result;
}
return dfs(0, false);
}
console.log(maxProfit([7,1,5,3,6,4])); // 7
Line Notes
const memo = new Map();Initialize cache for memoization
const key = i + ',' + holding;Create unique key for caching
if (memo.has(key)) return memo.get(key);Return cached result if present
memo.set(key, result);Store computed result
return result;Return result
Memoization ensures each state (day, holding) is computed once, total 2*n states.
💡 For n=10^5, this reduces calls from exponential to linear, making it efficient.
Interview Verdict: Accepted
Memoization makes the recursive approach practical for large inputs.