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(k) for Greedy
Understanding Time Complexity

We want to understand how different problem-solving methods affect the time it takes to run code.

Which method grows faster or slower as the input gets bigger?

Scenario Under Consideration

Analyze the time complexity of the following code snippet.


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

// Dynamic Programming Fibonacci (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 example: Coin change (not always optimal)
int GreedyCoinChange(int amount, int[] coins) {
  int count = 0;
  for (int i = coins.Length - 1; i >= 0; i--) {
    count += amount / coins[i];
    amount %= coins[i];
  }
  return count;
}
    

This code shows three ways to solve problems: simple recursion, dynamic programming with memory, and a greedy approach.

Identify Repeating Operations

Identify the loops, recursion, array traversals that repeat.

  • Primary operation: Recursive calls in FibRec, repeated subproblems in FibDP, and loop over coins in GreedyCoinChange.
  • How many times: FibRec calls itself many times exponentially; FibDP solves each subproblem once; Greedy loops once over coins.
How Execution Grows With Input

As input size grows, recursive calls in FibRec explode quickly, DP grows slowly, and Greedy stays fast.

Input Size (n)Approx. Operations FibRecApprox. Operations FibDPApprox. Operations Greedy
10~177 calls10 calls3 loops
20~21,891 calls20 calls3 loops
30~2,692,537 calls30 calls3 loops

Pattern observation: Simple recursion grows very fast, DP grows linearly, and Greedy grows with coin types, not input size.

Final Time Complexity

Time Complexity: O(2^n) for Recursion, O(n) for DP, O(k) for Greedy

Recursion can be very slow, DP is efficient by remembering results, and Greedy is fast but may not always be correct.

Common Mistake

[X] Wrong: "Greedy always gives the best answer quickly."

[OK] Correct: Greedy picks the best choice now but can miss better overall solutions, so it's not always correct.

Interview Connect

Knowing when to use recursion, DP, or greedy helps you solve problems efficiently and impress interviewers with smart choices.

Self-Check

"What if we added memoization to the greedy coin change? 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. Simple Recursion
B. Dynamic Programming
C. Greedy Algorithm
D. Brute Force

Solution

  1. Step 1: Understand problem characteristics

    Overlapping subproblems mean 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 B
  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 memoization
  • Confusing optimal substructure with greedy choice
2. Which of the following C# code snippets correctly implements a greedy approach to find the maximum number of activities that don't overlap given start and end times arrays (assuming sorted by end time)?
easy
A. int count = 1; int lastEnd = end[0]; for (int i = 1; i < start.Length; i++) { if (start[i] >= lastEnd) { count++; lastEnd = end[i]; } } Console.WriteLine(count);
B. int count = 0; for (int i = 0; i < start.Length; i++) { if (start[i] > end[i]) count++; } Console.WriteLine(count);
C. int count = 1; for (int i = 1; i < start.Length; i++) { if (end[i] >= start[i-1]) count++; } Console.WriteLine(count);
D. int count = 0; foreach (var s in start) { count += s; } Console.WriteLine(count);

Solution

  1. Step 1: Understand greedy activity selection logic

    We select the first activity, then pick next activities starting after the last selected activity ends.
  2. Step 2: Check code correctness

    int count = 1; int lastEnd = end[0]; for (int i = 1; i < start.Length; i++) { if (start[i] >= lastEnd) { count++; lastEnd = end[i]; } } Console.WriteLine(count); initializes count to 1, tracks lastEnd, and increments count only if start[i] >= lastEnd, updating lastEnd. This matches greedy logic.
  3. Final Answer:

    the code that tracks lastEnd -> Option A
  4. Quick Check:

    Greedy activity selection code = int count = 1; int lastEnd = end[0]; for (int i = 1; i < start.Length; i++) { if (start[i] >= lastEnd) { count++; lastEnd = end[i]; } } Console.WriteLine(count); [OK]
Hint: Pick next activity starting after last selected ends [OK]
Common Mistakes:
  • Not sorting activities by end time before selection
  • Comparing start[i] with wrong end time
  • Counting activities incorrectly
3. What is the output of the following C# code that uses recursion to calculate Fibonacci numbers without memoization?
int Fib(int n) { if (n <= 1) return n; return Fib(n - 1) + Fib(n - 2); } Console.WriteLine(Fib(5));
medium
A. 3
B. 8
C. 5
D. 13

Solution

  1. Step 1: Trace recursive calls for Fib(5)

    Fib(5) = Fib(4) + Fib(3); Fib(4) = Fib(3) + Fib(2); Fib(3) = Fib(2) + Fib(1); Fib(2) = Fib(1) + Fib(0); Fib(1)=1, Fib(0)=0.
  2. Step 2: Calculate values bottom-up

    Fib(0)=0, Fib(1)=1, Fib(2)=1, Fib(3)=2, Fib(4)=3, Fib(5)=5.
  3. Final Answer:

    5 -> Option C
  4. Quick Check:

    Fibonacci(5) = 5 [OK]
Hint: Fibonacci(5) = 5 by definition [OK]
Common Mistakes:
  • Confusing index starting at 0 or 1
  • Miscomputing recursive sums
  • Assuming memoization changes output
4. The following C# code attempts to solve the coin change problem using recursion but has a bug. What is the bug?
int CoinChange(int[] coins, int amount) { if (amount == 0) return 0; if (amount < 0) return -1; int min = int.MaxValue; foreach (int coin in coins) { int res = CoinChange(coins, amount - coin); if (res >= 0 && res < min) min = 1 + res; } return min == int.MaxValue ? -1 : min; }
medium
A. No memoization causing repeated calculations
B. Incorrect base case for amount == 0
C. Returning -1 instead of 0 for amount < 0
D. Using foreach instead of for loop

Solution

  1. Step 1: Analyze recursion and base cases

    Base cases are correct: 0 returns 0, negative returns -1. The recursion tries all coins.
  2. Step 2: Identify missing optimization

    Without memoization, the function recalculates subproblems many times, causing inefficiency and possible stack overflow.
  3. Final Answer:

    No memoization causing repeated calculations -> Option A
  4. Quick Check:

    Missing memoization = No memoization causing repeated calculations [OK]
Hint: Add memo dictionary to avoid repeated calls [OK]
Common Mistakes:
  • Thinking base cases are wrong
  • Believing loop type causes bug
  • Ignoring performance issues
5. You want to find the minimum number of coins needed to make a given amount using coins of different denominations. Which approach is best and why?
hard
A. Greedy algorithm may fail for some coin sets
B. Simple iteration misses subproblem reuse
C. Recursion without memoization is inefficient
D. Dynamic programming ensures optimal solution efficiently

Solution

  1. Step 1: Evaluate greedy approach limitations

    Greedy works only if coin denominations are canonical (like US coins). Otherwise, it can fail to find minimum coins.
  2. Step 2: Consider recursion and iteration drawbacks

    Recursion without memoization repeats work; simple iteration without storing results misses subproblem reuse.
  3. Step 3: Understand dynamic programming benefits

    DP with bottom-up tabulation stores minimum coins for all amounts up to target, guaranteeing optimal and efficient solution.
  4. Final Answer:

    Dynamic programming ensures optimal solution efficiently -> Option D
  5. Quick Check:

    Minimum coins problem = DP bottom-up [OK]
Hint: Use DP tabulation for guaranteed minimum coins [OK]
Common Mistakes:
  • Assuming greedy always works
  • Ignoring performance of recursion
  • Not storing intermediate results
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