Welcome back. In our previous lesson, we tackled the exponential complexity of the naive recursive Fibonacci solution by introducing memoization. This "top-down" dynamic programming approach preserved the recursive structure while using a cache to store subproblem results, bringing the time complexity down to a swift linear .
Today, we will explore the second fundamental approach to dynamic programming: tabulation. This "bottom-up" method eliminates recursion entirely, building the solution iteratively from the ground up. Our goals are to implement a tabulation-based solution for the Fibonacci sequence, analyze its performance, and draw a clear comparison between it and memoization. This will equip you with two powerful and distinct perspectives for solving DP problems.
From Top-Down to Bottom-Up: The Tabulation Philosophy
In memoization, we started with the problem fib(n) and let recursion break it down, catching and storing results along the way. Tabulation flips this script. It embodies a "bottom-up" philosophy:
- Identify the smallest, simplest subproblems (the base cases).
- Solve them first.
- Use their solutions to iteratively solve slightly larger subproblems.
- Continue this process until you have built your way up to the solution for the original problem,
fib(n).
Instead of a recursion tree that branches downwards, imagine building a table of results, filling it row by row from the beginning. This iterative nature makes tabulation straightforward to implement with a simple loop, which has the added benefit of avoiding the overhead and potential stack depth limitations of recursion.
The following video from Abdul Bari provides an excellent illustration of this bottom-up construction.
4 Principle of Optimality - Dynamic Programming introduction
This video masterfully demonstrates the iterative, bottom-up nature of tabulation.
Watch the segment from the iterative function for Fibonacci. Observe how he uses an array and a loop to fill the table starting from the base cases F(0) and F(1) and building up to F(5).
Implementing Tabulation
To translate the tabulation approach into C++ code, we follow a clear, three-step process. We will use a std::vector to serve as our "table."
The "takeuforward" article provides a concise guide to these steps.
Dynamic Programming Introduction - Tutorial
This article breaks down the tabulation method into simple, actionable steps.
Please read Part -2: Tabulation. Focus on the three steps listed, as they provide a general template for any bottom-up DP solution.
Let's formalize these steps for our Fibonacci problem:
-
Declare a DP Array: Create a vector,
dp, of sizen + 1. This vector will store the Fibonacci numbers from 0 ton. For example,dp[i]will hold the value of the i-th Fibonacci number. -
Initialize Base Cases: Set the initial known values directly in the array. For Fibonacci, these are
dp[0] = 0anddp[1] = 1. -
Iterate and Fill: Loop from
i = 2up ton. In each iteration, calculatedp[i]using the values we have already computed, which are stored in ourdparray. The recurrence relation tells usdp[i] = dp[i-1] + dp[i-2].
After the loop completes, the answer to our problem, fib(n), is simply the value stored at dp[n].
The following C++ code from the cp-algorithms resource demonstrates this elegant implementation.
Introduction to Dynamic Programming
This resource shows a clean, iterative C++ implementation.
In the section Bottom-up Dynamic Programming, examine the C++ code block that follows. Notice how it directly implements the three steps: array creation, base case initialization, and the iterative loop.
Memoization vs. Tabulation: A Detailed Comparison
You now know two ways to solve the same DP problem. While they often achieve the same time complexity, their underlying mechanisms and trade-offs are different. Understanding these distinctions is crucial for choosing the right approach in a competitive programming context.
| Aspect | Memoization (Top-Down) | Tabulation (Bottom-Up) |
|---|---|---|
| Approach | Starts with the final state f(n) and recursively breaks it down. |
Starts with the base cases f(0), f(1) and iteratively builds up to f(n). |
| Implementation | Recursive. Modifies a plain recursive function by adding a cache. | Iterative. Typically uses for loops. No recursion. |
| State Evaluation | Computes only the states required to solve the problem. | Computes all states from the base case up to n. |
| Stack Space | Uses recursion stack space, which can be an issue for deep recursions (can cause Stack Overflow). Space: . | No recursion stack is used. Space is constant in this regard. |
| Overhead | Function call overhead for each recursive step. | Generally faster due to no recursive call overhead. |
For the Fibonacci problem, both methods have a time complexity of . However, tabulation's space complexity is (for the DP array), while memoization's is (for the DP array) + (for the recursion stack), which still simplifies to but has a larger constant factor and the risk of stack overflow. For these reasons, tabulation is often preferred in competitive programming.
The take U forward video offers another great breakdown of this comparison.
DP 1. Introduction to Dynamic Programming | Memoization | Tabulation | Space Optimization Techniques
This video provides a step-by-step conversion from the recursive logic to a tabulation implementation.
Watch the segment from converting to tabulation. The presenter clearly outlines the logic for building the bottom-up solution and compares its time and space complexity to the memoized version, highlighting the elimination of the recursion stack space.
A Final Touch: Space Optimization
Let's re-examine the tabulation recurrence: dp[i] = dp[i-1] + dp[i-2].
Notice that to compute the value for the current index i, we only need the values at i-1 and i-2. We never need dp[i-3] or any earlier values again. This suggests that storing the entire dp array is wasteful. We can optimize the space complexity from to !
The idea is to use just two variables to track the previous two values. Let's call them prev2 (for fib(i-2)) and prev (for fib(i-1)).
The process inside the loop becomes:
- Calculate
current = prev + prev2. - Update the variables for the next iteration:
prev2becomes whatprevwas, andprevbecomes thecurrentvalue we just calculated. - Repeat until we reach
n.

This simple optimization is a powerful technique you'll see often. The take U forward resources provide a clear explanation and implementation of this idea.
Dynamic Programming Introduction - Tutorial
This section explains and codes the space-optimization technique.
Read Part 3: Space Optimization. Pay close attention to the illustration and how the variables prev, prev2, and curr are updated within the loop. The C++ code block below shows this in action.
After this optimization, our Fibonacci solution has a time complexity of and a space complexity of —a significant improvement and the most efficient solution for this problem.
Conclusion
In this lesson, you've learned the bottom-up approach to dynamic programming, tabulation. By starting with base cases and iteratively building up the solution, we eliminated recursion and its associated overhead. We then took it a step further, optimizing the space from linear to constant.
Key Takeaways:
- Tabulation is a bottom-up, iterative DP method that builds solutions from base cases.
- It is often implemented with a
forloop and a DP array/vector, avoiding the risk of stack overflow. - For Fibonacci, both memoization and tabulation achieve time, but tabulation is slightly more space-efficient by not using the recursion stack.
- The space for tabulation can often be optimized. For Fibonacci, by observing that we only need the last two results, we reduced space complexity from to .
You are now familiar with the two main pillars of dynamic programming. In our next lesson, we will begin applying these foundational patterns to solve classic competitive programming problems, starting with a 1D DP problem called "Climbing Stairs".