Memoization Top Down DP in DSA Python - 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 results change the number of steps as input grows?
Analyze the time complexity of the following Fibonacci function using memoization.
def fib(n, memo=None):
if memo is None:
memo = {}
if n in memo:
return memo[n]
if n <= 1:
return n
memo[n] = fib(n-1, memo) + fib(n-2, memo)
return memo[n]
This code calculates the nth Fibonacci number by storing results to avoid repeated work.
Identify the loops, recursion, array traversals that repeat.
- Primary operation: Recursive calls to fib with smaller n values.
- 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 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 compute grows in a straight line as the input number increases.
[X] Wrong: "Memoization still does all recursive calls like the simple recursive method, so it's slow."
[OK] Correct: Memoization saves results, so each unique call happens once, avoiding repeated work and making it much faster.
Understanding memoization shows you can improve slow recursive solutions by saving work, a key skill in many coding problems.
"What if we removed memoization and just 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 multiple times.Step 2: Compare options
Only To store results of subproblems to avoid repeated calculations describes this purpose correctly. Other options describe unrelated actions.Final Answer:
To store results of subproblems to avoid repeated calculations -> Option AQuick Check:
Memoization = Store subproblem results [OK]
- Confusing memoization with sorting or iteration
- Thinking memoization increases input size
- Believing memoization converts recursion to iteration
Solution
Step 1: Identify data structure for memo
Memoization requires a dictionary to store key-value pairs of subproblem inputs and results.Step 2: Match with options
Only memo = {} uses a dictionary ({}). Lists, sets, and tuples do not support key-value storage.Final Answer:
memo = {} -> Option BQuick Check:
Memo dictionary = {} [OK]
- Using list or set instead of dictionary
- Confusing tuple with dictionary
- Not initializing memo before recursion
def fib(n, memo={}):
if n in memo:
return memo[n]
if n <= 1:
return n
memo[n] = fib(n-1, memo) + fib(n-2, memo)
return memo[n]
print(fib(5))Solution
Step 1: Understand Fibonacci calculation with memo
fib(5) calculates Fibonacci number at position 5 using memo to save results.Step 2: Calculate fib(5)
fib(0)=0, fib(1)=1, fib(2)=1, fib(3)=2, fib(4)=3, fib(5)=5.Final Answer:
5 -> Option CQuick Check:
fib(5) = 5 [OK]
- Confusing Fibonacci sequence values
- Expecting 8 instead of 5 for fib(5)
- Thinking code causes error due to default dict
def fib(n, memo={}):
if n <= 1:
return n
if n in memo:
return memo[n]
memo[n] = fib(n-1, memo) + fib(n-2, memo)
return memo[n]Solution
Step 1: Analyze default argument usage
Using memo={} as default argument creates shared dictionary across calls, causing unexpected behavior.Step 2: Identify correct practice
Default mutable arguments should be avoided; better to use None and initialize inside function.Final Answer:
Default mutable argument causes shared state across calls -> Option DQuick Check:
Mutable default argument = bug [OK]
- Ignoring mutable default argument issue
- Thinking base case is missing
- Assuming memo dictionary usage is wrong
n steps, taking 1 or 2 steps at a time. Which memoized top-down function correctly solves this problem?Solution
Step 1: Understand problem and base cases
Number of ways to climb n steps with 1 or 2 steps is classic DP with base cases: ways(0)=1, ways(-1)=0.Step 2: Check each option for correctness
def climb(n, memo=None): if memo is None: memo = {} if n < 0: return 0 if n == 0: return 1 if n in memo: return memo[n] memo[n] = climb(n-1, memo) + climb(n-2, memo) return memo[n] correctly initializes memo, handles negative and zero cases, and uses memoization properly. Others have wrong base cases or memo usage.Final Answer:
Option A function correctly solves the problem -> Option AQuick Check:
Proper base cases + memo dict = correct solution [OK]
- Wrong base case returns
- Using mutable default memo argument
- Using list instead of dict for memo
Ready to prove it?
Pass the test and this pattern moves unknown
