Why Dynamic Programming and When Recursion Alone Fails
📖 Scenario: Imagine you want to find the number of ways to climb a staircase with 5 steps. You can climb either 1 or 2 steps at a time. This is a common problem where recursion alone can be slow because it repeats the same calculations many times.We will learn how to use dynamic programming to avoid repeating work and make the solution faster.
🎯 Goal: Build a simple program that first uses recursion to count ways to climb stairs, then improves it using dynamic programming with memoization to avoid repeated calculations.
📋 What You'll Learn
Create a recursive function to count ways to climb stairs
Add a memo object to store results of subproblems
Modify the recursive function to use the memo object
Print the number of ways to climb 5 steps
💡 Why This Matters
🌍 Real World
Counting ways to do tasks with overlapping steps appears in many real-world problems like route planning, resource allocation, and game moves.
💼 Career
Understanding when recursion is inefficient and how dynamic programming optimizes it is key for software engineers solving complex algorithm problems.
Progress0 / 4 steps
1
Create a recursive function to count ways to climb stairs
Create a function called countWays that takes a number n representing steps left. If n is 0 or 1, return 1. Otherwise, return the sum of countWays(n - 1) and countWays(n - 2). Then call countWays(5) and store the result in a variable called result.
DSA Typescript
Hint
Think about the base cases for 0 and 1 steps. Then use recursion to add the ways for smaller steps.
2
Add a memo object to store results of subproblems
Create an empty object called memo to store previously calculated results for each n.
DSA Typescript
Hint
This object will help us remember answers for each step count to avoid repeating work.
3
Modify the recursive function to use the memo object
Change the countWays function to first check if n is in memo. If yes, return memo[n]. Otherwise, calculate the result recursively, store it in memo[n], and then return it.
DSA Typescript
Hint
Use the memo object to save results and check before computing again.
4
Print the number of ways to climb 5 steps
Add a console.log statement to print the value of countWays(5).
DSA Typescript
Hint
The output should be the number of ways to climb 5 steps, which is 8.
Practice
(1/5)
1. Why do we use dynamic programming instead of just recursion for some problems?
easy
A. Because recursion alone can repeat the same calculations many times, making it slow.
B. Because recursion cannot solve problems with multiple steps.
C. Because dynamic programming uses loops instead of functions.
D. Because recursion always causes errors in large problems.
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 A
Quick Check:
Recursion repeats work, DP saves results [OK]
Hint: DP saves repeated results to speed up recursion [OK]
Common Mistakes:
Thinking recursion cannot handle multiple steps
Believing DP only uses loops
Assuming recursion always causes errors
2. Which of the following TypeScript function signatures correctly shows a recursive function with memoization parameter for dynamic programming?
easy
A. function fib(n: number): number[]
B. function fib(n: string, memo: number): number
C. function fib(memo: number[], n: number): void
D. function fib(n: number, memo: number[] = []): number
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 D
Quick Check:
Correct memo param with default empty array [OK]
Hint: Memo param usually an array with default empty value [OK]
Common Mistakes:
Swapping parameter order
Using wrong return type
Not providing default value for memo
3. What is the output of this TypeScript code snippet?
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));
4. Identify the bug in this recursive Fibonacci function without memoization:
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));
medium
A. It will cause a stack overflow error for large n.
B. It returns wrong Fibonacci numbers for n > 2.
C. It uses memoization incorrectly.
D. It has a syntax error in the return statement.
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 A
Quick Check:
Deep recursion without memo causes stack overflow [OK]
Hint: Large n without memo causes stack overflow [OK]
Common Mistakes:
Thinking it returns wrong values
Assuming syntax error exists
Confusing memoization usage
5. You want to find the minimum number of coins to make a sum using recursion. Why is dynamic programming necessary here instead of plain recursion?
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;
}
hard
A. Because recursion cannot handle arrays as input parameters.
B. Because recursion alone recalculates the same sums multiple times, causing slow performance.
C. Because dynamic programming uses loops instead of recursion.
D. Because recursion always returns Infinity for large sums.
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 B
Quick Check:
DP saves repeated sum calculations [OK]
Hint: DP caches results to avoid repeated sum calculations [OK]
Common Mistakes:
Thinking recursion can't handle arrays
Believing DP replaces recursion with loops
Assuming recursion returns Infinity always
Recommended
Ready to prove it?
Pass the test and this pattern moves unknownstrongwhile earning +1 Grip toward your certificate.