Why Dynamic Programming and When Recursion Alone Fails in DSA Typescript - Performance Analysis
We want to see why simple recursion can be slow for some problems and how dynamic programming helps speed things up.
How does the number of repeated calculations affect the time it takes?
Analyze the time complexity of this recursive Fibonacci function.
function fib(n: number): number {
if (n <= 1) return n;
return fib(n - 1) + fib(n - 2);
}
This code calculates the nth Fibonacci number using simple recursion without storing results.
Look at what repeats in this code.
- Primary operation: Calling fib recursively twice for each number above 1.
- How many times: Many calls repeat the same fib values again and again, especially for smaller numbers.
As n grows, the number of calls grows very fast because each call makes two more calls.
| Input Size (n) | Approx. Operations |
|---|---|
| 10 | About 177 calls |
| 20 | Over 21,000 calls |
| 30 | More than 2 million calls |
Pattern observation: The calls roughly double with each increase in n, causing very fast growth.
Time Complexity: O(2^n)
This means the time needed doubles with each step up in n, making it very slow for large inputs.
[X] Wrong: "Recursion always solves problems efficiently because it breaks them down."
[OK] Correct: Without remembering past results, recursion repeats the same work many times, causing slow performance.
Understanding when recursion alone is slow and how dynamic programming improves it shows your skill in writing efficient code for real problems.
"What if we add a memory to store results (memoization)? How would the time complexity change?"
Practice
Solution
Step 1: Understand recursion's limitation
Recursion solves problems by calling itself but often recalculates the same results multiple times.Step 2: Recognize dynamic programming's advantage
Dynamic programming stores results of subproblems to avoid repeated work, making it faster.Final Answer:
Because recursion alone can repeat the same calculations many times, making it slow. -> Option AQuick Check:
Recursion repeats work, DP saves results [OK]
- Thinking recursion cannot handle multiple steps
- Believing DP only uses loops
- Assuming recursion always causes errors
Solution
Step 1: Check parameter types and order
function fib(n: number, memo: number[] = []): number has number n and optional memo array with default empty array, suitable for memoization.Step 2: Verify return type and parameters
function fib(n: number, memo: number[] = []): number returns number (the Fibonacci number), others have wrong types or order.Final Answer:
function fib(n: number, memo: number[] = []): number -> Option DQuick Check:
Correct memo param with default empty array [OK]
- Swapping parameter order
- Using wrong return type
- Not providing default value for memo
function fib(n: number, memo: 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];
}
console.log(fib(5));Solution
Step 1: Trace fib(5) calls with memo
fib(5) = fib(4) + fib(3), memo stores results to avoid repeats.Step 2: Calculate fib values
fib(0)=0, fib(1)=1, fib(2)=1, fib(3)=2, fib(4)=3, fib(5)=5.Final Answer:
5 -> Option CQuick Check:
Fibonacci of 5 = 5 [OK]
- Confusing fib(5) with fib(6)
- Ignoring memoization effect
- Assuming output is sum of all calls
function fib(n: number): number {
if (n === 0) return 0;
if (n === 1) return 1;
return fib(n - 1) + fib(n - 2);
}
console.log(fib(40));Solution
Step 1: Analyze recursion depth for fib(40)
Without memoization, fib(40) causes many repeated calls, leading to deep recursion.Step 2: Understand consequences
Deep recursion can cause stack overflow or very slow execution, but no syntax or logic errors.Final Answer:
It will cause a stack overflow error for large n. -> Option AQuick Check:
Deep recursion without memo causes stack overflow [OK]
- Thinking it returns wrong values
- Assuming syntax error exists
- Confusing memoization usage
function minCoins(coins: number[], sum: number): number {
if (sum === 0) return 0;
let res = Infinity;
for (let coin of coins) {
if (coin <= sum) {
let subRes = minCoins(coins, sum - coin);
if (subRes !== Infinity && subRes + 1 < res) {
res = subRes + 1;
}
}
}
return res;
}Solution
Step 1: Identify repeated subproblems in recursion
The function calls itself with the same sums multiple times, recalculating results.Step 2: Explain dynamic programming benefit
DP stores results for sums already computed, avoiding repeated work and speeding up solution.Final Answer:
Because recursion alone recalculates the same sums multiple times, causing slow performance. -> Option BQuick Check:
DP saves repeated sum calculations [OK]
- Thinking recursion can't handle arrays
- Believing DP replaces recursion with loops
- Assuming recursion returns Infinity always
Ready to prove it?
Pass the test and this pattern moves unknown
