Memoization to Optimize Recursion in DSA Java - Time & Space Complexity
We want to see how adding memoization changes the speed of recursive functions.
How does remembering past answers help us do less work?
Analyze the time complexity of the following code snippet.
public class Fibonacci {
private int[] memo;
public Fibonacci(int n) {
memo = new int[n + 1];
}
public int fib(int n) {
if (n <= 1) return n;
if (memo[n] != 0) return memo[n];
memo[n] = fib(n - 1) + fib(n - 2);
return memo[n];
}
}
This code calculates Fibonacci numbers using recursion and stores results to avoid repeated work.
Identify the loops, recursion, array traversals that repeat.
- Primary operation: Recursive calls to fib for smaller numbers.
- How many times: Without memo, calls grow exponentially; with memo, each number is computed once.
With memoization, each Fibonacci number from 0 to n is calculated once and stored.
| 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 input size.
Time Complexity: O(n)
This means the time to compute grows directly with the input size, much faster than without memoization.
[X] Wrong: "Memoization makes recursion instantly fast for all problems."
[OK] Correct: Memoization only helps if the problem has overlapping subproblems; otherwise, it won't reduce calls.
Understanding memoization shows you can improve slow recursive solutions by saving work, a key skill in many coding challenges.
"What if we removed the memo array 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 so they can be reused instead of recalculated.Final Answer:
To store results of expensive function calls and reuse them -> Option AQuick Check:
Memoization = Store and reuse results [OK]
- Thinking memoization makes code shorter
- Confusing memoization with iteration
- Believing memoization increases recursion calls
Solution
Step 1: Identify suitable data structure for key-value pairs
Memoization requires storing results by input keys, so a Map is ideal.Step 2: Check Java syntax correctness
Map memo = new HashMap<>(); uses Map with HashMap, which is correct syntax for memoization cache.Final Answer:
Map memo = new HashMap<>(); -> Option AQuick Check:
Memo cache = Map for key-value pairs [OK]
- Using List instead of Map for memoization
- Incorrect array declaration syntax
- Using Set which stores only keys, no values
import java.util.*;
public class FibMemo {
static Map memo = new HashMap<>();
public static int fib(int n) {
if (n <= 1) return n;
if (memo.containsKey(n)) return memo.get(n);
int result = fib(n-1) + fib(n-2);
memo.put(n, result);
return result;
}
public static void main(String[] args) {
System.out.println(fib(5));
}
}Solution
Step 1: Understand Fibonacci sequence calculation
fib(5) = fib(4) + fib(3), and so on, with base cases fib(0)=0, fib(1)=1.Step 2: Calculate fib(5) with memoization
fib(2)=1, fib(3)=2, fib(4)=3, fib(5)=5. Memoization avoids repeated calls but result is same.Final Answer:
5 -> Option CQuick Check:
fib(5) = 5 [OK]
- Confusing Fibonacci values for wrong n
- Assuming memoization changes output
- Thinking code causes runtime error
import java.util.*;
public class FactorialMemo {
static Map memo = new HashMap<>();
public static int fact(int n) {
if (n == 0) return 1;
if (memo.containsKey(n)) return memo.get(n);
int result = n * fact(n - 1);
memo.put(n, result);
return result;
}
public static void main(String[] args) {
System.out.println(fact(-1));
}
}Solution
Step 1: Analyze base cases and input handling
Base case handles n == 0 only, but negative n causes no stop condition.Step 2: Effect of negative input
Calling fact(-1) leads to infinite recursion because n never reaches 0 or memoized value.Final Answer:
No base case for negative input causes infinite recursion -> Option DQuick Check:
Missing negative base case = infinite recursion [OK]
- Ignoring negative input handling
- Assuming memo initialization error
- Thinking multiplication order causes error
n stairs taking 1 or 2 steps at a time. Which memoization approach below correctly implements this in Java?Solution
Step 1: Understand problem and memoization needs
We must store number of ways for each stair number to avoid repeated calculations.Step 2: Evaluate memoization approaches
Use a Map to store results; check cache before recursive calls; store results after calculation. uses Map to store and check results before recursion, which is correct memoization practice.Final Answer:
Use a Map to store results; check cache before recursive calls; store results after calculation. -> Option BQuick Check:
Memo = Map with cache check and store [OK]
- Not checking cache before recursive calls
- Using List or Set incorrectly for memoization
- Storing results too late or not at all
Ready to prove it?
Pass the test and this pattern moves unknown
