Step 1: Call fib(5), check memo, not found, so compute fib(4) and fib(3)
memo: [] (empty), call stack: fib(5) -> fib(4) -> fib(3)
Why: We start from the top problem and break it down
Step 2: Call fib(4), check memo, not found, compute fib(3) and fib(2)
memo: [] (empty), call stack: fib(5) -> fib(4) -> fib(3) -> fib(2)
Why: Keep breaking down until base cases
Step 3: Call fib(3), check memo, not found, compute fib(2) and fib(1)
memo: [] (empty), call stack: fib(5) -> fib(4) -> fib(3) -> fib(2) -> fib(1)
Why: Continue breaking down
Step 4: fib(2): fib(1) base returns 1, fib(0) base returns 0, fib(2)=1+0=1, store memo[2] = 1
memo: [_, _, 1, _, _, _], call stack unwinding
Why: fib(2) computed from base cases, store result
Step 5: fib(1) base case returns 1 (no store)
memo: [_, _, 1, _, _, _], call stack unwinding
Why: Base case reached, return result without storing
Step 6: fib(3) = fib(2) + fib(1) = 1 + 1 = 2, store memo[3] = 2
memo: [_, _, 1, 2, _, _]
Why: Combine stored results to get fib(3)
Step 7: fib(2) called again, found in memo as 1, no recomputation
memo: [_, _, 1, 2, _, _]
Why: Reuse stored result to save time
Step 8: fib(4) = fib(3) + fib(2) = 2 + 1 = 3, store memo[4] = 3
memo: [_, _, 1, 2, 3, _]
Why: Combine stored results to get fib(4)
Step 9: fib(3) called again, found in memo as 2, no recomputation
memo: [_, _, 1, 2, 3, _]
Step 10: fib(5) = fib(4) + fib(3) = 3 + 2 = 5, store memo[5] = 5
memo: [_, _, 1, 2, 3, 5]
Why: Final answer computed using stored results
Result: memo: [_, _, 1, 2, 3, 5]
Fibonacci(5) = 5