Memoization to Optimize Recursion in DSA C++ - Time & Space Complexity
We want to see how using memoization changes the speed of recursive functions.
How does remembering past answers help us do less work?
Analyze the time complexity of the following code snippet.
#include <iostream>
#include <vector>
using namespace std;
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];
}
int main() {
int n = 10;
vector<int> memo(n + 1, -1);
cout << fib(n, memo) << endl;
return 0;
}
This code calculates the nth Fibonacci number using recursion with memoization to store results.
Identify the loops, recursion, array traversals that repeat.
- Primary operation: Recursive calls to fib with memo checks.
- How many times: Each unique fib(n) is computed once; repeated calls return stored results immediately.
Each number from 0 to n is calculated once and saved, so work grows 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: Doubling n roughly doubles the work, showing linear growth.
Time Complexity: O(n)
This means the time to finish grows directly with the input size, much faster than plain recursion.
[X] Wrong: "Memoization still does all recursive calls like plain recursion."
[OK] Correct: Memoization saves results so repeated calls don't redo work, cutting down the total calls drastically.
Understanding how memoization changes recursion speed is a key skill that shows you can improve algorithms smartly.
"What if we removed memoization and just used plain recursion? How would the time complexity change?"
Practice
memoization in recursive functions?Solution
Step 1: Understand what memoization does
Memoization saves the results of function calls so that if the same inputs occur again, the function can return the saved result instead of recalculating.Step 2: Identify the effect on performance
This reuse of results reduces repeated work and speeds up the function.Final Answer:
To store results of expensive function calls and reuse them -> Option DQuick Check:
Memoization = Store and reuse results [OK]
- Thinking memoization slows down the function
- Confusing memoization with converting recursion to loops
- Believing memoization removes all variables
Solution
Step 1: Identify suitable container for memoization
We need a container that can store integer results indexed by integer keys. std::vector<int> initialized with -1 is common for fixed size memoization.Step 2: Check syntax correctness
std::vector<int> memo(100, -1); correctly declares a vector of size 100 with all values -1, which can be used to check if a value is computed.Final Answer:
std::vector<int> memo(100, -1); -> Option BQuick Check:
Vector with size and default value = Correct memo cache [OK]
- Using incomplete or wrong container syntax
- Not initializing memo with default values
- Using map without specifying key and value types
int fib(int n, std::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];
}
int main() {
std::vector<int> memo(6, -1);
std::cout << fib(5, memo);
return 0;
}Solution
Step 1: Understand Fibonacci calculation with memoization
fib(5) calculates Fibonacci number at position 5 using memo to avoid repeated calls.Step 2: Calculate fib(5)
Fibonacci sequence: 0,1,1,2,3,5,... fib(5) = 5.Final Answer:
5 -> Option CQuick Check:
fib(5) = 5 [OK]
- Confusing fib(5) with fib(6)
- Assuming vector size too small causes error
- Ignoring memoization effect on output
int factorial(int n, std::vector<int>& memo) {
if (n == 0) return 1;
if (memo[n] != -1) return memo[n];
memo[n] = n * factorial(n - 1, memo);
return memo[n];
}
int main() {
std::vector<int> memo(5, -1);
std::cout << factorial(5, memo);
return 0;
}What is the error in this code?
Solution
Step 1: Check vector size for memoization
The vector memo has size 5, so valid indices are 0 to 4. But factorial(5) tries to access memo[5], which is out of range.Step 2: Identify consequence of out-of-range access
Accessing memo[5] causes undefined behavior or runtime error.Final Answer:
Vector size is too small causing out-of-range access -> Option AQuick Check:
Memo vector size < n causes error [OK]
- Ignoring vector size and indexing
- Thinking base case is wrong
- Assuming memo check is missing
n stairs, taking 1 or 2 steps at a time. Which memoization approach below correctly applies to this problem in C++?Solution
Step 1: Understand memoization requirements
Memo must store results for all n from 0 to n, so vector size n+1 with default -1 is correct.Step 2: Check memo usage in recursion
Before recursive calls, check if memo[n] is computed (not -1). If yes, return memo[n]. Otherwise, compute and store.Step 3: Analyze options
Use a vector<int> memo(n+1, -1) and store results for each stair count int ways(int n, std::vector<int>& memo) { if (n == 0) return 1; if (n < 0) return 0; if (memo[n] != -1) return memo[n]; memo[n] = ways(n-1, memo) + ways(n-2, memo); return memo[n]; } correctly implements memoization with vector size n+1, default -1, and memo check before recursion.Final Answer:
Option A code correctly applies memoization for climbing stairs problem -> Option AQuick Check:
Memo vector size n+1 and check before recursion = Correct [OK]
- Using wrong vector size causing out-of-range errors
- Not checking memo before recursive calls
- Storing results only for some stairs (odd/even)
Ready to prove it?
Pass the test and this pattern moves unknown
