Dynamic Programming: Memoization vs. Tabulation
Quick Overview
Plain recursion recomputes `F(3)` on both branches of `F(5)`. Storing each result once collapses an exponential call tree into a linear pass: memoisation top-down, tabulation bottom-up.
Deep Dive
1. The Core DP Philosophy
Dynamic Programming (DP) is an optimization technique that solves complex problems by breaking them down into overlapping subproblems and caching intermediate solutions so each subproblem is solved exactly once.
2. The Two Necessary Properties for DP
- Overlapping Subproblems: The same subproblems are solved repeatedly (e.g. Fibonacci:
F(5) = F(4) + F(3), whereF(3)is computed in both branches). - Optimal Substructure: The optimal solution to the problem can be constructed from optimal solutions to its subproblems.
3. Top-Down DP (Memoization)
Start with natural recursion and store computed results in a cache:
// Climbing Stairs: You can take 1 or 2 steps. How many ways to reach step N?
function climbStairsMemo(n, memo = new Map()) {
if (n <= 2) return n;
if (memo.has(n)) return memo.get(n); // Return cached result!
const result = climbStairsMemo(n - 1, memo) + climbStairsMemo(n - 2, memo);
memo.set(n, result);
return result;
}
console.log(climbStairsMemo(5)); // Output: 8
4. Bottom-Up DP (Tabulation)
Eliminate recursion entirely by filling an iterative table from base cases upward:
function climbStairsTab(n) {
if (n <= 2) return n;
const dp = new Array(n + 1);
dp[1] = 1;
dp[2] = 2;
for (let i = 3; i <= n; i++) {
dp[i] = dp[i - 1] + dp[i - 2];
}
return dp[n];
}
// Space Optimized: O(1) Space!
function climbStairsOptimized(n) {
if (n <= 2) return n;
let prev2 = 1, prev1 = 2;
for (let i = 3; i <= n; i++) {
const current = prev1 + prev2;
prev2 = prev1;
prev1 = current;
}
return prev1;
}
5. Comparison Matrix
| Strategy | Approach | Call Stack Space | Execution Speed |
|---|---|---|---|
| Top-Down (Memoization) | Recursive | O(N) call stack | Easy to formulate from intuition |
| Bottom-Up (Tabulation) | Iterative | O(1) / O(N) | Fastest, zero call stack overhead |
6. Common Mistakes
- Applying DP When Subproblems Do Not Overlap: Divide and conquer (e.g. Merge Sort) breaks problems into independent, non-overlapping subproblems. DP only applies when subproblems overlap!
7. Next Steps
Identify state transitions and base cases. Next, let's explore the 0/1 Knapsack Pattern!
Next Up in Data Structures & Algorithms Core Patterns
Big O Notation: Time & Space Complexity
Big O describes how work grows, not how long it takes. An `O(N^2)` algorithm on 1,000 items does a million steps; the same work at `O(N log N)` does about 10,000.
Dynamic Arrays & Amortized Resizing
A dynamic array doubles rather than growing by one, so the expensive copy lands on the 1st, 2nd, 4th and 8th push. Averaged across every push, that is `O(1)` amortised.
Two Pointers Pattern (Converging & Symmetrical)
A nested loop tests every pair in `O(N^2)`. On sorted data, moving two pointers inward from both ends rules out half the remaining candidates at each step and settles it in one `O(N)` pass.