Memoization Top Down DP in DSA C++ - Time & Space Complexity
We want to understand how using memoization in top-down dynamic programming affects the time it takes to solve problems.
Specifically, how does remembering past answers change the number of steps as input grows?
Analyze the time complexity of the following memoized Fibonacci function.
int fib(int n, vector<int>& memo) {
if (n <= 1) return n;
if (memo[n] != -1) return memo[n];
memo[n] = fib(n-1, memo) + fib(n-2, memo);
return memo[n];
}
This code calculates the nth Fibonacci number using recursion and stores results to avoid repeated work.
Identify the loops, recursion, array traversals that repeat.
- Primary operation: Recursive calls to fib with smaller n.
- How many times: Each unique n from 0 to n is computed once and then reused.
Each number from 0 up to n is calculated once, so the steps grow roughly in a straight line with n.
| Input Size (n) | Approx. Operations |
|---|---|
| 10 | About 10 calculations |
| 100 | About 100 calculations |
| 1000 | About 1000 calculations |
Pattern observation: The work grows directly with n, not faster.
Time Complexity: O(n)
This means the time to finish grows in a straight line as the input number n grows.
[X] Wrong: "Memoized recursion still does all calls like plain recursion, so it's exponential time."
[OK] Correct: Memoization saves results so each unique call happens once, cutting down repeated work drastically.
Understanding how memoization changes time complexity shows you can improve slow recursive solutions, a key skill in many coding challenges.
"What if we removed memoization and used plain recursion? How would the time complexity change?"
Practice
Solution
Step 1: Understand memoization concept
Memoization saves the results of subproblems so they are not recalculated.Step 2: Identify its role in top-down DP
In top-down DP, memoization avoids repeated work by caching answers.Final Answer:
To store results of subproblems to avoid repeated calculations -> Option CQuick Check:
Memoization = store subproblem results [OK]
- Confusing memoization with sorting
- Thinking memoization reduces memory by deleting variables
- Believing memoization converts recursion to iteration
Solution
Step 1: Check array initialization
int memo[1000] = {0}; declares an array of size 1000 and initializes all elements to 0.Step 2: Compare other options
int memo[1000]; declares but does not initialize; C is invalid syntax in C++; D is invalid syntax for array.Final Answer:
int memo[1000] = {0}; -> Option BQuick Check:
Correct array declaration with initialization = int memo[1000] = {0}; [OK]
- Not initializing memo array causing garbage values
- Using invalid syntax for array declaration
- Confusing vector initialization with array
int memo[100] = {0};
int fib(int n) {
if (n <= 1) return n;
if (memo[n] != 0) return memo[n];
memo[n] = fib(n-1) + fib(n-2);
return memo[n];
}
int main() {
std::cout << fib(5);
return 0;
}Solution
Step 1: Trace fib(5) calls
fib(5) = fib(4) + fib(3), fib(4) = 3, fib(3) = 2, so fib(5) = 5.Step 2: Confirm memoization usage
Memo stores results to avoid repeated calls but does not change output.Final Answer:
5 -> Option AQuick Check:
fib(5) = 5 [OK]
- Confusing fib(5) with fib(6)
- Ignoring base cases n <= 1
- Assuming memo array starts with all zeros means zero output
int memo[100] = {0};
int fib(int n) {
if (n == 0) return 0;
if (n == 1) return 1;
if (memo[n] != 0) return memo[n];
memo[n] = fib(n-1) + fib(n-2);
return memo[n];
}Solution
Step 1: Trace fib(2)
fib(2) calls fib(1) and fib(0), returns 1 + 0 = 1 correctly, memo[2] = 1.Step 2: Check memo condition
Base cases are before memo check, so fib(0)=0 and fib(1)=1 always handled directly. For n>=2, fib(n) > 0.Step 3: No error present
Code produces correct results; using -1 init is better practice generally but not required here.Final Answer:
No error, code works correctly -> Option AQuick Check:
fib(2) = 1 correctly [OK]
- Assuming zero means unvisited memo entry
- Not handling base cases properly in memoization
- Ignoring that fib(0) = 0 causes memo check issues
countWays(4)? int memo[100] = {-1};
int countWays(int n) {
if (n == 0) return 1;
if (n < 0) return 0;
if (memo[n] != -1) return memo[n];
memo[n] = countWays(n-1) + countWays(n-2);
return memo[n];
}
int main() {
std::cout << countWays(4);
return 0;
}Solution
Step 1: Understand problem and base cases
countWays(0) = 1 means one way to stand still; countWays(n) sums ways from n-1 and n-2 steps.Step 2: Calculate countWays(4)
countWays(4) = countWays(3) + countWays(2) countWays(3) = countWays(2) + countWays(1) countWays(2) = countWays(1) + countWays(0) countWays(1) = countWays(0) + countWays(-1) = 1 + 0 = 1 countWays(0) = 1 So, countWays(2) = 1 + 1 = 2 countWays(3) = 2 + 1 = 3 countWays(4) = 3 + 2 = 5Final Answer:
5 -> Option DQuick Check:
Ways to climb 4 stairs = 5 [OK]
- Forgetting base case countWays(0) = 1
- Miscounting negative step returns
- Not initializing memo with -1 to mark unvisited
Ready to prove it?
Pass the test and this pattern moves unknown
