Memoization to Optimize Recursion in DSA Typescript - Time & Space Complexity
We want to see how using memoization changes the speed of recursive functions.
How does remembering past answers help us avoid repeating work?
Analyze the time complexity of the following code snippet.
function fib(n: number, memo: Record<number, number> = {}): number {
if (n <= 1) return n;
if (memo[n] !== undefined) return memo[n];
memo[n] = fib(n - 1, memo) + fib(n - 2, memo);
return memo[n];
}
This function calculates the nth Fibonacci number using recursion and stores results to avoid repeated calculations.
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 due to memoization.
Each number from 0 up to n is calculated once and stored, so work grows steadily with n.
| Input Size (n) | Approx. Operations |
|---|---|
| 10 | About 10 calculations |
| 100 | About 100 calculations |
| 1000 | About 1000 calculations |
Pattern observation: The work grows roughly in a straight line as n increases.
Time Complexity: O(n)
This means the function does work proportional to the input size, calculating each needed value once.
[X] Wrong: "Recursive Fibonacci always takes exponential time because it calls itself twice each time."
[OK] Correct: With memoization, each number is calculated once and saved, so repeated calls don't redo work, making it much faster.
Understanding how memoization changes recursion speed is a key skill that shows you can improve algorithms by avoiding repeated work.
"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 recursion inefficiency
Recursive functions often repeat the same calculations multiple times, which wastes time.Step 2: Role of memoization
Memoization saves the results of these repeated calculations in a storage (like an object) so they can be reused without recalculating.Final Answer:
To store results of expensive function calls and reuse them later -> Option DQuick Check:
Memoization = Store and reuse results [OK]
- Thinking memoization slows down the function
- Confusing memoization with iteration
- Believing memoization removes recursion
Solution
Step 1: Identify memo storage type
Memoization requires a storage that maps input (number) to output (number), so an object with number keys and number values is suitable.Step 2: Check TypeScript syntax
Recordcorrectly types an object with number keys and number values.Final Answer:
const memo: Record = {} -> Option BQuick Check:
Memo object type = Record [OK]
- Using array instead of object for memo
- Declaring memo as a number or string
- Not initializing memo as an empty object
function fib(n: number, memo: Record = {}): number {
if (n <= 1) return n;
if (memo[n]) return memo[n];
memo[n] = fib(n - 1, memo) + fib(n - 2, memo);
return memo[n];
}
console.log(fib(5));Solution
Step 1: Understand Fibonacci sequence
Fibonacci numbers start as 0,1,1,2,3,5,8,... The 5th Fibonacci number (0-based) is 5.Step 2: Trace fib(5) call
fib(5) = fib(4) + fib(3) = 3 + 2 = 5, memoization avoids repeated calls.Final Answer:
5 -> Option AQuick Check:
fib(5) = 5 [OK]
- Confusing index starting at 0 or 1
- Ignoring memoization effect on output
- Calculating fib(5) as fib(6)
function factorial(n: number, memo: Record = {}): number {
if (n === 0) return 1;
if (memo[n]) return memo[n];
memo[n] = n * factorial(n - 1, memo);
return memo[n];
}
console.log(factorial(5));Solution
Step 1: Understand memo check issue
Checkingif (memo[n])fails when memo[n] is 0 or falsy, but factorial values are always positive, so 0 is not possible here, but better to check for undefined explicitly.Step 2: Fix condition to check existence properly
Useif (memo[n] !== undefined)to correctly detect cached values.Final Answer:
Changeif (memo[n])toif (memo[n] !== undefined)-> Option CQuick Check:
Check memo existence with !== undefined [OK]
- Using if(memo[n]) which fails for falsy cached values
- Adding unnecessary base case for n=1
- Removing memo parameter breaks memoization
n stairs taking 1 or 2 steps at a time. Which memoization approach below correctly implements this in TypeScript?Solution
Step 1: Understand problem base cases
Number of ways to climb 0 or 1 stairs is 1 (one way: stand still or one step).Step 2: Check memoization and recursion logic
function climb(n: number, memo: Record = {}): number { if (n <= 1) return 1; if (memo[n] !== undefined) return memo[n]; memo[n] = climb(n - 1, memo) + climb(n - 2, memo); return memo[n]; } correctly uses memo object, checks with !== undefined, and sums ways from n-1 and n-2 stairs.Step 3: Identify errors in other options
function climb(n: number): number { if (n <= 1) return 1; return climb(n - 1) + climb(n - 2); } lacks memoization, function climb(n: number, memo: number[] = []): number { if (n <= 1) return 1; if (memo[n]) return memo[n]; memo[n] = climb(n - 1, memo) * climb(n - 2, memo); return memo[n]; } multiplies instead of sums, function climb(n: number, memo: Record = {}): number { if (n === 0) return 0; if (memo[n]) return memo[n]; memo[n] = climb(n - 1, memo) + climb(n - 2, memo); return memo[n]; } returns 0 for n=0 which is incorrect.Final Answer:
Option A code correctly implements memoized climb function -> Option AQuick Check:
Memo + sum of previous two = correct climb ways [OK]
- Forgetting memoization causing slow recursion
- Multiplying instead of adding ways
- Wrong base case for n=0
Ready to prove it?
Pass the test and this pattern moves unknown
