Why Greedy Works and When It Fails in DSA Go - 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.
package main
import "fmt"
func greedyCoinChange(coins []int, amount int) int {
count := 0
for i := len(coins) - 1; i >= 0; i-- {
count += amount / coins[i]
amount %= coins[i]
}
return count
}
func main() {
coins := []int{1, 5, 10, 25}
amount := 63
fmt.Println(greedyCoinChange(coins, amount))
}
This code picks the largest coin first and uses as many as possible before moving to smaller coins.
Look at the loops and repeated steps:
- Primary operation: Loop over the coin types once.
- How many times: Once per coin type, so number of coins (k).
The time depends on how many coin types we have, not the amount.
| Input Size (number of coins k) | Approx. Operations |
|---|---|
| 4 | 4 steps |
| 10 | 10 steps |
| 100 | 100 steps |
Pattern observation: The work grows linearly with the number of coin types, not the amount to change.
Time Complexity: O(k)
This means the algorithm runs quickly as long as the number of coin types is small, regardless of the amount.
[X] Wrong: "Greedy always finds the best answer for any coin set."
[OK] Correct: Greedy can fail if coin values don't fit nicely, missing the minimum coins needed.
Understanding when greedy works helps you choose the right approach quickly and explain your reasoning clearly in interviews.
"What if the coin denominations were not sorted or included unusual values? How would that affect the greedy algorithm's time complexity and correctness?"
Practice
Solution
Step 1: Understand greedy choice property
A greedy algorithm makes the best local choice at each step, aiming for a global best solution.Step 2: Differentiate from exhaustive search
Unlike trying all solutions or recursion, greedy does not explore all paths but relies on local optimality.Final Answer:
Because it makes the best choice at each step hoping for a global optimum -> Option CQuick Check:
Greedy choice property = Because it makes the best choice at each step hoping for a global optimum [OK]
- Confusing greedy with exhaustive search
- Thinking greedy always tries all solutions
- Assuming sorting alone makes greedy correct
Solution
Step 1: Identify correct max initialization and comparison
Initialize max with first element and update if current value is greater.Step 2: Check loop and condition correctness
max := nums[0] for _, v := range nums { if v > max { max = v } } uses correct range loop and updates max when v > max.Final Answer:
Option A correctly finds the largest number -> Option AQuick Check:
Correct max update condition = max := nums[0] for _, v := range nums { if v > max { max = v } } [OK]
- Using '<' instead of '>' to find max
- Initializing max to zero instead of first element
- Comparing incorrectly inside loop
package main
import "fmt"
func main() {
intervals := [][]int{{1,4},{3,5},{0,6},{5,7},{8,9},{5,9}}
selected := [][]int{}
lastEnd := 0
for _, interval := range intervals {
if interval[0] >= lastEnd {
selected = append(selected, interval)
lastEnd = interval[1]
}
}
fmt.Println(selected)
}Solution
Step 1: Understand greedy selection by earliest finish time
Intervals are checked in given order; select if start >= last selected end.Step 2: Trace selection step-by-step
Selected intervals: [1,4] (lastEnd=4), skip [3,5], skip [0,6], select [5,7] (lastEnd=7), select [8,9].Final Answer:
[[1 4] [5 7] [8 9]] -> Option AQuick Check:
Greedy interval selection = [[1 4] [5 7] [8 9]] [OK]
- Not updating lastEnd correctly
- Selecting overlapping intervals
- Assuming input is sorted by finish time
package main
import "fmt"
func main() {
coins := []int{25, 10, 5, 1}
amount := 30
count := 0
for _, coin := range coins {
count += amount / coin
amount = amount % coin
}
fmt.Println(count)
}Solution
Step 1: Analyze greedy coin change logic
The code picks largest coins first, counting how many fit, updating amount correctly.Step 2: Understand greedy failure cases
Greedy works for US coins but fails for some coin sets; code logic is correct but approach can fail.Final Answer:
The greedy approach fails for some coin sets, but code logic is correct -> Option BQuick Check:
Greedy correctness depends on coin set = The greedy approach fails for some coin sets, but code logic is correct [OK]
- Thinking code has syntax or logic error
- Assuming coin order must be ascending
- Resetting count inside loop incorrectly
Solution
Step 1: Recall interval scheduling greedy strategy
The proven greedy method is to pick meetings finishing earliest to maximize remaining time.Step 2: Compare other strategies
Picking earliest start or shortest duration can cause overlaps and miss optimal count.Final Answer:
Always pick the meeting with the earliest finish time -> Option DQuick Check:
Earliest finish time strategy = Always pick the meeting with the earliest finish time [OK]
- Choosing earliest start time instead of finish time
- Picking shortest duration meetings incorrectly
- Assuming latest start time works best
Ready to prove it?
Pass the test and this pattern moves unknown
