Memoization (top-down DP) augments a recursive solution with a cache so each subproblem is solved only once; it is natural to implement but uses the call stack. Tabulation (bottom-up DP) fills a table iteratively from base cases, avoiding recursion entirely. Both achieve the same asymptotic complexity but differ in which subproblems are computed — memoization computes only subproblems needed for the query, while tabulation computes all subproblems in a fixed order. Tabulation also enables space optimizations by discarding rows of the DP table that are no longer needed.
Implement both approaches for the same 2-3 canonical problems: Fibonacci, coin change, and longest common subsequence. Verify identical results and compare space usage. Then optimize the tabulation version to use O(n) space instead of O(n²) for LCS.
Dynamic programming solves problems by reusing solutions to overlapping subproblems, but the way you organize that reuse leads to two fundamentally different implementation strategies. Memoization (top-down) starts from the original problem and recurses downward, caching each subproblem's result the first time it is computed. Tabulation (bottom-up) starts from the smallest base cases and iteratively builds up to the answer. Both eliminate redundant computation, but they differ in control flow, memory usage, and practical tradeoffs.
Consider computing the nth Fibonacci number. A naive recursive implementation recomputes F(3) exponentially many times when calculating F(50). With memoization, you wrap the same recursive function with a lookup — typically a hash map or array. Before computing F(k), check the cache; if the result is there, return it immediately. Otherwise compute it recursively, store the result, and return. The recursion tree that would have exploded into billions of calls collapses to exactly n unique subproblems, each solved once. The structure of your original recursion stays untouched — you are just adding a memory layer on top.
Tabulation takes the opposite approach. You allocate an array of size n, set the base cases (F(0) = 0, F(1) = 1), and fill forward: F(i) = F(i-1) + F(i-2) for each i from 2 to n. There is no recursion, no call stack, and no hash map overhead. Because you control the iteration order explicitly, you can see that each entry depends only on the two previous entries — so you can drop the full array and keep just two variables, reducing space from O(n) to O(1). This kind of space optimization is tabulation's signature advantage. In two-dimensional DP problems like longest common subsequence, the same logic lets you reduce an O(mn) table to two O(n) arrays, since each row depends only on the row before it.
The tradeoff between the two strategies is practical, not theoretical. Memoization is easier to write when the recurrence is complex or the subproblem space is sparse — it naturally avoids computing subproblems that are never needed. Tabulation is preferred when every subproblem will be visited anyway, when you want to eliminate recursion depth limits (Python's default stack, for example, caps at around 1000 frames), or when space optimization matters. In interviews and competitive programming, memoization gets you a correct solution faster; in production systems processing large inputs, tabulation with space optimization is usually the better engineering choice. Mastering both lets you pick the right tool depending on the constraints you face.
No topics depend on this one yet.