Let's transform a problem through ALL FOUR stages using House Robber as our example.
Problem: Given array [2, 7, 9, 3, 1], find max money without robbing adjacent houses.
Answer: 12 (rob houses with values 2, 9, 1)
STAGE 1: PURE RECURSION (Brute Force)
Thought process:
- At each house, I decide: ROB or SKIP
- If ROB: take money + solve for 2 houses later (can't rob adjacent)
- If SKIP: solve for next house
- Return the maximum
Time: O(2^n) - each house branches into 2 choices
Space: O(n) - recursion stack
Decision Tree for [2, 7, 9, 3, 1]:
rob(0)
/ \
ROB[0] SKIP[0]
+2 |
| |
rob(2) rob(1)
/ \ / \
ROB[2] SKIP ROB[1] SKIP
+9 | +7 |
| | | |
rob(4) rob(3) rob(3) rob(2) <- OVERLAPPING!
+1 ... ... ...
See rob(2) and rob(3) computed multiple times!
STAGE 2: MEMOIZATION (Top-Down DP)
Key insight: Same subproblems solved repeatedly!
Solution: Cache results in a memo array
Before computing rob(i):
1. Check if already in memo -> return cached
2. Otherwise compute, store in memo, return
Time: O(n) - each state computed only once
Space: O(n) - memo array + recursion stack
Execution trace with memo:
rob(0): not in memo
-> rob(2): not in memo
-> rob(4): not in memo, compute = 1, memo[4] = 1
-> rob(3): not in memo, compute = 3, memo[3] = 3
-> rob(2) = max(9+1, 3) = 10, memo[2] = 10
-> rob(1): not in memo
-> rob(3): IN MEMO! return 3 <- CACHE HIT!
-> rob(2): IN MEMO! return 10 <- CACHE HIT!
-> rob(1) = max(7+3, 10) = 10, memo[1] = 10
-> rob(0) = max(2+10, 10) = 12, memo[0] = 12
Answer: 12
STAGE 3: TABULATION (Bottom-Up DP)
Key insight: Instead of recursing, build solution from base cases UP
Process:
1. Define dp[i] = max money from house i to end
2. Start from END (base cases)
3. Work backwards to START
Time: O(n)
Space: O(n) - just the dp array (no recursion stack!)
Filling the DP table:
nums = [2, 7, 9, 3, 1]
Start from right:
dp[4] = 1 (only house 4)
dp[3] = max(3+0, 1) = 3 (rob 3 OR skip to 4)
dp[2] = max(9+1, 3) = 10 (rob 2 OR skip to 3)
dp[1] = max(7+3, 10) = 10 (rob 1 OR skip to 2)
dp[0] = max(2+10, 10) = 12 (rob 0 OR skip to 1)
dp = [12, 10, 10, 3, 1]
↑
Answer!
STAGE 4: SPACE OPTIMIZATION
Key insight: dp[i] only needs dp[i+1] and dp[i+2]
We don't need the entire array!
Use just 2 variables:
- next1 = dp[i+1] (skip to next house)
- next2 = dp[i+2] (rob current, skip adjacent)
Time: O(n)
Space: O(1) <- OPTIMAL!
Trace with 2 variables:
nums = [2, 7, 9, 3, 1]
Start: next1 = 0, next2 = 0
i=4: curr = max(1+0, 0) = 1
next2 = 0, next1 = 1
i=3: curr = max(3+0, 1) = 3
next2 = 1, next1 = 3
i=2: curr = max(9+1, 3) = 10
next2 = 3, next1 = 10
i=1: curr = max(7+3, 10) = 10
next2 = 10, next1 = 10
i=0: curr = max(2+10, 10) = 12 <- ANSWER!
next2 = 10, next1 = 12
Final answer: 12
Summary: The 4 Stages
┌─────────────────┬────────────┬────────────┐
│ Stage │ Time │ Space │
├─────────────────┼────────────┼────────────┤
│ 1. Recursion │ O(2^n) │ O(n) │
│ 2. Memoization │ O(n) │ O(n) │
│ 3. Tabulation │ O(n) │ O(n) │
│ 4. Optimized │ O(n) │ O(1) │
└─────────────────┴────────────┴────────────┘
Progression:
Brute Force -> Cache Results -> Iterate -> Minimize Variables