Memoization Top Down DP in DSA Java - 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 answers change the work needed as input grows?
Analyze the time complexity of the following code snippet.
public int fib(int n, 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];
}
This code calculates the nth Fibonacci number using top-down recursion with memoization 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 and stored.
Each number from 0 up to n is calculated once, then reused from memory.
| 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 with n, not exponentially.
Time Complexity: O(n)
This means the time to compute grows directly with the input size, thanks to remembering past results.
[X] Wrong: "Recursive Fibonacci always takes exponential time because it calls itself twice each time."
[OK] Correct: Memoization stores results, so each number is only calculated once, avoiding repeated work.
Understanding how memoization changes time complexity shows you can improve slow recursive solutions, a key skill in problem solving.
"What if we removed memoization and just used plain recursion? How would the time complexity change?"
Practice
Solution
Step 1: Understand memoization role
Memoization saves answers of smaller problems so they are not recalculated.Step 2: Compare options
Only To store results of subproblems to avoid repeated calculations correctly describes storing subproblem results to avoid repeats.Final Answer:
To store results of subproblems to avoid repeated calculations -> Option CQuick Check:
Memoization = store subproblem results [OK]
- Confusing memoization with sorting
- Thinking memoization changes recursion to iteration
- Believing memoization increases input size
Solution
Step 1: Check Java array declaration syntax
Correct syntax for array with size n+1 is: int[] memo = new int[n+1];Step 2: Identify errors in other options
int memo = new int[n]; declares int, not array; C misses size; D uses wrong constructor syntax.Final Answer:
int[] memo = new int[n+1]; -> Option DQuick Check:
Array declaration = int[] memo = new int[size] [OK]
- Declaring memo as int instead of int[]
- Not specifying array size when creating
- Using parentheses instead of brackets for arrays
public class FibMemo {
static int[] memo;
public static int fib(int n) {
if (n <= 1) return n;
if (memo[n] != -1) return memo[n];
memo[n] = fib(n-1) + fib(n-2);
return memo[n];
}
public static void main(String[] args) {
int n = 5;
memo = new int[n+1];
for (int i = 0; i <= n; i++) memo[i] = -1;
System.out.println(fib(n));
}
}Solution
Step 1: Understand Fibonacci calculation with memo
fib(5) = fib(4) + fib(3), recursively calculated and stored in memo.Step 2: Calculate fib(5) step-by-step
fib(0)=0, fib(1)=1, fib(2)=1, fib(3)=2, fib(4)=3, fib(5)=5.Final Answer:
5 -> Option AQuick Check:
fib(5) = 5 [OK]
- Confusing fib(5) with fib(6)
- Forgetting to initialize memo with -1
- Assuming memo causes errors if not initialized
static int fib(int n, int[] memo) {
if (n <= 1) return n;
if (memo[n] != 0) return memo[n];
memo[n] = fib(n-1, memo) + fib(n-2, memo);
return memo[n];
}
public static void main(String[] args) {
int n = 4;
int[] memo = new int[n+1];
System.out.println(fib(n, memo));
}Solution
Step 1: Check memo initialization and cache check
Memo array defaults to 0; fib(0)=0, so memo[n]!=0 check fails for fib(0).Step 2: Explain why -1 initialization is needed
Initialize memo with -1 to distinguish uncalculated from zero results.Final Answer:
Memo array not initialized with -1, causing wrong cache checks -> Option AQuick Check:
Initialize memo with -1 to avoid false cache hits [OK]
- Assuming 0 means unvisited in memo
- Changing base case incorrectly
- Using wrong memo array size
public class CoinChange {
static int[] memo;
public static int countWays(int amount, int[] coins) {
if (amount == 0) return 1;
if (amount < 0) return 0;
if (memo[amount] != -1) return memo[amount];
int ways = 0;
for (int coin : coins) {
ways += countWays(amount - coin, coins);
}
memo[amount] = ways;
return ways;
}
public static void main(String[] args) {
int[] coins = {1, 2};
int amount = 4;
memo = new int[amount + 1];
for (int i = 0; i <= amount; i++) memo[i] = -1;
System.out.println(countWays(amount, coins));
}
}Solution
Step 1: Understand problem and recursion
Count ways to make 4 using coins 1 and 2, summing ways for amount-coin recursively.Step 2: Calculate ways for amount=4
Ways(4) = Ways(3)+Ways(2); Ways(3)=Ways(2)+Ways(1); Ways(2)=Ways(1)+Ways(0); Ways(1)=Ways(0)+Ways(-1); base cases: Ways(0)=1, Ways(-1)=0.Step 3: Calculate values bottom-up
Ways(0)=1; Ways(1)=1+0=1; Ways(2)=1+1=2; Ways(3)=2+1=3; Ways(4)=3+2=5.Step 4: Re-check sum
Actually, the code counts permutations, so for amount=4 with coins {1,2}, total ways = 5.Final Answer:
5 -> Option BQuick Check:
Number of ways to make 4 with {1,2} = 5 [OK]
- Confusing combinations with permutations
- Not initializing memo with -1
- Miscounting base cases
Ready to prove it?
Pass the test and this pattern moves unknown
