Bird
Raised Fist0
DSA C++programming~5 mins

DP vs Recursion vs Greedy Choosing the Right Tool in DSA C++ - Complexity Comparison

Choose your learning style10 modes available
Time Complexity: DP vs Recursion vs Greedy Choosing the Right Tool
O(2^n) for recursion, O(n) for DP, O(m) for greedy
Understanding Time Complexity

Choosing between dynamic programming, recursion, and greedy methods affects how fast your code runs.

We want to see how the number of steps changes with input size for each approach.

Scenario Under Consideration

Analyze the time complexity of these three approaches solving the same problem.


// Recursive Fibonacci (simple recursion)
int fibRec(int n) {
  if (n <= 1) return n;
  return fibRec(n-1) + fibRec(n-2);
}

// Dynamic Programming (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 approach (not suitable here, but example)
// For problems where local choice leads to global optimum
// Example: coin change with unlimited coins and greedy coin set
int greedyCoinChange(int amount, int coins[], int size) {
  int count = 0;
  for (int i = size - 1; i >= 0; i--) {
    while (amount >= coins[i]) {
      amount -= coins[i];
      count++;
    }
  }
  return count;
}
    

This code shows recursive, dynamic programming, and greedy methods for different problems.

Identify Repeating Operations

Look at the loops and calls that repeat work.

  • Recursive Fibonacci: Calls itself twice per call, many repeated calculations.
  • Dynamic Programming: Each subproblem solved once, stored in memo array.
  • Greedy Coin Change: Loops over coins and subtracts from amount repeatedly.
  • Dominant operation: Recursive calls for recursion, single pass for DP and greedy.
How Execution Grows With Input

See how steps increase as input size grows.

Input Size (n)Recursive Calls / StepsDP Calls / StepsGreedy Steps
10~177 calls10 callsDepends on amount and coins
20~21,891 calls20 callsDepends on amount and coins
30~2,692,537 calls30 callsDepends on amount and coins

Recursive calls grow very fast (exponentially), DP grows linearly, greedy depends on input but usually linear.

Final Time Complexity

Time Complexity: O(2^n) for recursion, O(n) for DP, O(m) for greedy

Recursion repeats work many times, DP avoids repeats, greedy quickly picks choices.

Common Mistake

[X] Wrong: "Recursion is always slow and should be avoided."

[OK] Correct: Recursion can be efficient with memoization (DP) and is sometimes the simplest way to express a solution.

Interview Connect

Understanding these differences helps you pick the right tool and explain your choices clearly in interviews.

Self-Check

"What if we changed the recursive Fibonacci to use bottom-up iteration instead of memoization? How would the time complexity change?"

Practice

(1/5)
1. Which approach is best suited when a problem has overlapping subproblems and optimal substructure?
easy
A. Dynamic Programming
B. Greedy Algorithm
C. Simple Recursion without memoization
D. Brute Force

Solution

  1. Step 1: Understand problem characteristics

    Overlapping subproblems means the same smaller problems repeat. Optimal substructure means the best solution can be built from best solutions of smaller problems.
  2. Step 2: Match approach to characteristics

    Dynamic Programming stores results of subproblems to avoid recomputation, ideal for overlapping subproblems and optimal substructure.
  3. Final Answer:

    Dynamic Programming -> Option A
  4. Quick Check:

    Overlapping subproblems + optimal substructure = Dynamic Programming [OK]
Hint: Use DP when subproblems repeat and build optimal solutions [OK]
Common Mistakes:
  • Choosing greedy when subproblems overlap
  • Using simple recursion without memo causing slow code
  • Confusing optimal substructure with greedy choice property
2. Which of the following is the correct syntax to implement memoization in C++ recursion?
easy
A. Use a while loop instead of recursion
B. Use a for loop inside the recursive function
C. Call the recursive function twice without storing results
D. Use a global array or vector to store results and check before recursion

Solution

  1. Step 1: Understand memoization concept

    Memoization stores results of recursive calls to avoid repeated work.
  2. Step 2: Identify correct syntax usage

    Using a global or passed array/vector to save results and checking it before recursion is the correct way.
  3. Final Answer:

    Use a global array or vector to store results and check before recursion -> Option D
  4. Quick Check:

    Memoization = store results before recursion [OK]
Hint: Memoize by storing results in array before recursive calls [OK]
Common Mistakes:
  • Not storing results causing repeated calculations
  • Using loops inside recursion incorrectly
  • Calling recursion multiple times without memo
3. What is the output of the following C++ code snippet?
int fib(int n) {
  if (n <= 1) return n;
  return fib(n-1) + fib(n-2);
}

int main() {
  std::cout << fib(4);
  return 0;
}
medium
A. 4
B. 5
C. 3
D. 7

Solution

  1. Step 1: Trace fib(4) calls

    fib(4) = fib(3) + fib(2) - fib(3) = fib(2) + fib(1) = (fib(1)+fib(0)) + 1 = (1+0)+1=2 - fib(2) = fib(1) + fib(0) = 1 + 0 = 1 So fib(4) = 2 + 1 = 3
  2. Step 2: Confirm output

    The program prints 3 as the result of fib(4).
  3. Final Answer:

    3 -> Option C
  4. Quick Check:

    fib(4) = 3 [OK]
Hint: Trace small inputs stepwise to find Fibonacci output [OK]
Common Mistakes:
  • Confusing fib(4) with fib(5)
  • Adding wrong Fibonacci numbers
  • Assuming fib(4) equals 4
4. Identify the error in the following code snippet implementing a greedy approach for coin change:
int coinChange(int amount, vector<int>& coins) {
  int count = 0;
  for (int i = 0; i < coins.size(); i++) {
    while (amount >= coins[i]) {
      amount -= coins[i];
      count++;
    }
  }
  return count;
}
medium
A. Coins vector is not sorted in descending order
B. Using while loop inside for loop is incorrect
C. Function should return amount, not count
D. No error, code is correct

Solution

  1. Step 1: Analyze greedy coin change logic

    Greedy coin change requires coins sorted descending to pick largest coin first.
  2. Step 2: Identify error in code

    The code loops coins in given order; if coins are not sorted descending, greedy fails to minimize count.
  3. Final Answer:

    Coins vector is not sorted in descending order -> Option A
  4. Quick Check:

    Greedy coin change needs descending sorted coins [OK]
Hint: Sort coins descending before greedy coin change [OK]
Common Mistakes:
  • Not sorting coins descending
  • Returning wrong variable
  • Thinking while loop inside for is wrong
5. You want to find the minimum number of coins to make a given amount using coins of denominations [1, 3, 4]. Which approach is best and why?
hard
A. Greedy approach because picking largest coin first always works
B. Dynamic Programming to store minimum coins needed for sub-amounts
C. Simple recursion without memoization to try all combinations
D. Brute force by checking all subsets of coins

Solution

  1. Step 1: Understand problem requirements

    We want minimum coins for amount using coins [1,3,4]. Greedy fails here because picking largest coin first may not give minimum coins.
  2. Step 2: Choose best approach

    Dynamic Programming stores minimum coins for all smaller amounts, building up to the target efficiently and correctly.
  3. Final Answer:

    Dynamic Programming to store minimum coins needed for sub-amounts -> Option B
  4. Quick Check:

    Minimum coins with tricky denominations = DP [OK]
Hint: Use DP when greedy fails to find minimum coins [OK]
Common Mistakes:
  • Assuming greedy always works for coin change
  • Using recursion without memo causing slow code
  • Trying brute force which is inefficient
Recommended

Ready to prove it?

Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard