Why Greedy Works and When It Fails in DSA C++ - Performance Analysis
We want to understand how fast greedy algorithms run and why they sometimes give the best answer quickly.
When does greedy work well, and when does it take longer or fail?
Analyze the time complexity of a simple greedy approach to the coin change problem.
#include <iostream>
#include <vector>
#include <algorithm>
int greedyCoinChange(std::vector<int> &coins, int amount) {
int count = 0;
std::sort(coins.rbegin(), coins.rend());
for (int coin : coins) {
count += amount / coin;
amount %= coin;
}
return amount == 0 ? count : -1;
}
This code tries to make change by always picking the largest coin first.
Look for loops or repeated steps that take most time.
- Primary operation: Loop over all coin types once.
- How many times: Once per coin type (usually small number).
The time depends mostly on how many coin types we have, not the amount.
| Input Size (number of coins) | Approx. Operations |
|---|---|
| 3 | 3 loops |
| 10 | 10 loops |
| 100 | 100 loops |
Pattern observation: The time grows linearly with the number of coin types.
Time Complexity: O(m log m) where m is number of coin types
This means the algorithm runs quickly, sorting coins then looping once over them.
[X] Wrong: "Greedy always finds the best answer fast for any coin set."
[OK] Correct: Some coin sets make greedy fail to find the fewest coins, so it may give wrong answers even if it runs fast.
Understanding when greedy works helps you pick the right tool quickly and explain your choices clearly in interviews.
"What if we replaced sorting with a fixed coin order? How would that affect the time complexity and correctness?"
Practice
Solution
Step 1: Understand greedy choice property
A greedy algorithm picks the best option available at each step without reconsidering previous choices.Step 2: Connect greedy choice to global optimum
This local best choice leads to a global optimum only if the problem has the greedy choice property and optimal substructure.Final Answer:
Because it makes the best choice at each step hoping for a global optimum -> Option AQuick Check:
Greedy = local best choice [OK]
- Thinking greedy tries all solutions
- Confusing greedy with backtracking
- Assuming sorting alone makes greedy work
Solution
Step 1: Identify correct C++ loop syntax for iteration
The range-based for loopfor (auto item : items)is modern and correct for iterating over a container.Step 2: Check other options for correctness
for (int i = 0 i < n; i++) { choose item[i]; } is missing semicolons and proper syntax. while (i <= n) { choose item[i]; i++; } uses while but lacks initialization shown. do { choose item[i]; i--; } while (i > 0); decrements i incorrectly.Final Answer:
for (auto item : items) { choose item; } -> Option AQuick Check:
Range-based for loop is correct syntax [OK]
- Missing semicolons in for loop
- Using wrong loop conditions
- Decrementing index when should increment
struct Interval {
int start, end;
};
bool compare(Interval a, Interval b) {
return a.end < b.end;
}
int main() {
std::vector<Interval> intervals = {{1,4}, {3,5}, {0,6}, {5,7}, {8,9}};
std::sort(intervals.begin(), intervals.end(), compare);
int count = 0, last_end = -1;
for (auto interval : intervals) {
if (interval.start >= last_end) {
count++;
last_end = interval.end;
}
}
std::cout << count << std::endl;
return 0;
}
What is the output of this program?Solution
Step 1: Sort intervals by end time
Sorted intervals by end: {1,4}, {3,5}, {0,6}, {5,7}, {8,9}Step 2: Select intervals greedily
Select {1,4} (count=1, last_end=4), skip {3,5} (start 3 < 4), skip {0,6} (start 0 < 4), select {5,7} (start 5 >= 4, count=2, last_end=7), select {8,9} (start 8 >= 7, count=3, last_end=9)Step 3: Count total selected intervals
Total selected intervals = 3Final Answer:
3 -> Option CQuick Check:
Greedy interval selection count = 3 [OK]
- Counting overlapping intervals
- Not sorting intervals correctly
- Updating last_end incorrectly
int coins[] = {25, 10, 5, 1};
int amount = 30;
int count = 0;
for (int i = 0; i < 4; i++) {
while (amount >= coins[i]) {
amount -= coins[i];
count++;
}
}
std::cout << count << std::endl;
What is the main issue with this greedy approach?Solution
Step 1: Understand greedy coin change approach
The code picks largest coin repeatedly until amount is reduced to zero.Step 2: Identify greedy failure cases
Greedy works for US coins but fails for some coin sets where smaller coins combined give fewer coins than greedy picks.Final Answer:
It may fail for some coin sets where greedy is not optimal -> Option BQuick Check:
Greedy coin change can fail [OK]
- Assuming greedy always gives minimum coins
- Confusing syntax errors with logic errors
- Ignoring infinite loop possibility
Solution
Step 1: Understand tie-breaking in activity selection
Greedy sorts by earliest finish time. Ties in end time use earliest start as tie-breaker.Step 2: Analyze effect on greedy selection
Picking the earliest start time among tied end times may block more future activities, reducing the total number of activities selected.Step 3: Confirm impact on optimality
This tie-break rule can cause the greedy algorithm to fail to find the maximum set of non-overlapping activities.Final Answer:
Because picking earliest start time among ties may block more future activities -> Option DQuick Check:
Tie-break can affect future selections [OK]
- Thinking tie-breaks do not affect optimality
- Assuming all tie-breaks are harmless
- Ignoring impact of start times on future choices
Ready to prove it?
Pass the test and this pattern moves unknown
