DP vs Recursion vs Greedy Choosing the Right Tool in DSA Java - Complexity Comparison
Choosing between dynamic programming, recursion, and greedy methods affects how fast your code runs.
We want to see how the time needed grows as the problem size grows for each approach.
Analyze the time complexity of these three approaches solving the same problem.
// Recursive Fibonacci
int fibRec(int n) {
if (n <= 1) return n;
return fibRec(n-1) + fibRec(n-2);
}
// DP Fibonacci with memoization
int fibDP(int n, int[] memo) {
if (n <= 1) return n;
if (memo[n] != -1) return memo[n];
memo[n] = fibDP(n-1, memo) + fibDP(n-2, memo);
return memo[n];
}
// Greedy-like approach (not suitable here, but example)
int fibGreedy(int n) {
if (n <= 1) return n;
int a = 0, b = 1;
for (int i = 2; i <= n; i++) {
int c = a + b;
a = b;
b = c;
}
return b;
}
These snippets calculate Fibonacci numbers using recursion, dynamic programming, and a greedy-like iterative method.
Look at how many times the main calculations repeat.
- Recursive: Calls itself twice per step, many repeated calculations.
- DP: Each number calculated once and stored, avoids repeats.
- Greedy-like: Simple loop from 2 to n, no repeats.
As n grows, see how operations increase.
| Input Size (n) | Recursive Calls | DP Calls | Greedy Iterations |
|---|---|---|---|
| 10 | ~177 | 11 | 9 |
| 100 | Huge (exponential) | 101 | 99 |
| 1000 | Very huge (exponential) | 1001 | 999 |
Recursive grows very fast, DP and greedy grow linearly with n.
Time Complexity: O(2^n) for recursion, O(n) for DP and greedy
Recursive repeats work many times, DP and greedy do each step once, so they are faster as n grows.
[X] Wrong: "Recursion is always slow and greedy is always fast."
[OK] Correct: Some greedy solutions don't work correctly, and recursion with memoization (DP) can be very efficient.
Understanding these differences helps you pick the right tool and explain your choice clearly in interviews.
"What if we added memoization to the recursive Fibonacci? How would the time complexity change?"
Practice
Solution
Step 1: Understand problem properties
Overlapping subproblems means the same smaller problems repeat. Optimal substructure means the best solution can be built from best solutions of smaller parts.Step 2: Match approach to properties
Dynamic Programming stores results of subproblems to avoid repeated work, ideal for these properties.Final Answer:
Dynamic Programming -> Option AQuick Check:
Overlapping subproblems + optimal substructure = Dynamic Programming [OK]
- Choosing greedy when subproblems overlap
- Using simple recursion without memoization
- Confusing optimal substructure with greedy choice property
Solution
Step 1: Check base case correctness
Factorial base case is 0! = 1, so if (n == 0) return 1 is correct.Step 2: Check recursive call and calculation
Factorial(n) = n * factorial(n-1) is correct recursive formula.Final Answer:
public static int factorial(int n) { if (n == 0) return 1; else return n * factorial(n - 1); } -> Option BQuick Check:
Correct base case and recursive multiplication = public static int factorial(int n) { if (n == 0) return 1; else return n * factorial(n - 1); } [OK]
- Wrong base case (e.g., returning 0 instead of 1)
- Using loops instead of recursion
- Returning addition instead of multiplication
public class Test {
public static int fib(int n) {
if (n <= 1) return n;
return fib(n - 1) + fib(n - 2);
}
public static void main(String[] args) {
System.out.println(fib(4));
}
}Solution
Step 1: Trace fib(4) calls
fib(4) = fib(3) + fib(2). fib(3) = fib(2) + fib(1), fib(2) = fib(1) + fib(0).Step 2: Calculate values bottom-up
fib(0)=0, fib(1)=1, so fib(2)=1+0=1, fib(3)=1+1=2, fib(4)=2+1=3.Final Answer:
3 -> Option AQuick Check:
Fibonacci(4) = 3 [OK]
- Confusing index starting at 0 or 1
- Adding wrong recursive results
- Assuming fib(4) = 5 (fibonacci(5))
public static int minCoins(int[] coins, int amount) {
int count = 0;
for (int coin : coins) {
while (amount >= coin) {
amount -= coin;
count++;
}
}
return count;
}Solution
Step 1: Understand greedy coin change logic
Greedy picks largest coin first to minimize count. Coins must be sorted descending for this to work.Step 2: Check code for sorting
Code does not sort coins, so if coins are unsorted, greedy picks smaller coins first, leading to wrong answer.Final Answer:
Coins array must be sorted in descending order -> Option CQuick Check:
Greedy coin change needs descending sorted coins [OK]
- Not sorting coins before greedy selection
- Using recursion unnecessarily
- Resetting count inside loop causing wrong count
int[] arr = {2,3,1,1,4};Solution
Step 1: Analyze problem nature
Minimum jumps problem can be solved efficiently by greedy because at each step, jumping to the farthest reachable index reduces total jumps.Step 2: Compare approaches
Recursion or DP works but are slower. Greedy gives optimal solution in linear time for this problem.Final Answer:
Use Greedy approach to jump to the farthest reachable index -> Option DQuick Check:
Minimum jumps = Greedy farthest jump [OK]
- Using recursion causing timeouts
- Overcomplicating with DP when greedy suffices
- Ignoring problem constraints for greedy validity
Ready to prove it?
Pass the test and this pattern moves unknown
