Greedy Algorithms and Where They Fail

Graphs and Algorithmic Techniques · 35 min

DSA · Greedy algorithms

Take the best move now, and never look back

Greedy commits to the locally best choice and never reconsiders. That is provably optimal for activity selection and fractional knapsack, and provably wrong for coin change with 1, 3 and 4. You will trace both, and learn the exchange argument that tells them apart.

Watch greedy lose to DP on 6 rupees
Make 6coins 1, 3 and 4
Greedy4 + 1 + 1 · three coins
Optimal3 + 3 · two coins

01 The idea

Commit to the best-looking step, and move on

A greedy algorithm builds an answer one choice at a time. At every point it takes whatever looks best right now by one fixed rule, adds it to the answer, and never reconsiders. No backtracking, no table of past results. That is why greedy code is short and fast: for most of the classics it is a sort followed by a single pass.

The catch is that "looks best right now" is a guess about the future. Sometimes the guess is provably safe — you can show that any optimal answer can be rewritten to begin with the greedy choice without getting worse. That rewriting is called an exchange argument, and it is the only thing separating a greedy algorithm from a hopeful one. When no exchange argument exists, greedy still runs, still returns something, and quietly returns the wrong thing.

Greedy takes the locally best choice and never undoes it. It is correct only when you can show that some optimal answer starts with that same choice.
Greedy-choice propertyThere is an optimal answer that contains the choice your rule makes first. If you cannot argue this, your algorithm is a guess wearing a proof-shaped hat.
Optimal substructureOnce you commit to a choice, what is left is a smaller instance of the same problem, and solving that part optimally gives an optimal whole.
Exchange argumentThe proof technique. Take any optimal answer, swap the greedy choice into it, and show the result is still valid and no smaller.

02 Worked example

Five events, one hall, sorted by finish time

Five events want the same seminar hall: A 1–5, B 4–6, C 5–9, D 0–10, E 9–12. You may run any set that does not overlap, and you want as many events as possible. Sort by the time each event finishes — these five already are — then walk the list once, taking an event whenever it starts at or after the hall is free. Green boxes are taken, rose boxes are skipped forever.

eventABCDE
starts at14509
ends at5691012
check AABCDE1 ≥ 0 · take · hall free at 5
check BABCDE4 < 5 · overlaps A · skip
check CABCDE5 ≥ 5 · take · hall free at 9
check DABCDE0 < 9 · overlaps · skip
check EABCDE9 ≥ 9 · take E · A C E = 3 events

Three events out of five, in one pass over the sorted list, with one number in memory. Everything rides on what the sort is keyed on. Finishing early is the only property that matters, because the event that frees the hall soonest leaves the most room for everything after it. How early an event starts and how long it runs are both irrelevant — and section 03 shows exactly what you lose by keying on those two instead.

03 The sorting key

Same loop, three keys, three different answers

The scan is three lines and never changes — lines 3 to 5 below. You hold one number, free, the time the hall is next available, and take an event when its start is at least that number. Only line 1 differs between these two versions. On the five events above, the left one returns 1 event and the right one returns 3.

Sorted by start time — wrong

sort events by start time ascendingfree = 0;  taken = []for e in events:    if e.start >= free:        taken.add(e);  free = e.endreturn taken

Sorted by finish time — correct

sort events by finish time ascendingfree = 0;  taken = []for e in events:    if e.start >= free:        taken.add(e);  free = e.endreturn taken
Sort keyOrder it producesGreedy takesResult
Earliest finishA B C D EA, C, E3 events
Earliest startD A B C ED only1 event
Shortest durationB E A C DB, E2 events

Why finish time is provably right. Let O be any optimal schedule and let f be the event that finishes earliest overall. If O does not start with f, swap its first event for f. Since f ends no later, nothing else in O can now clash, and the count is unchanged. So some optimal schedule starts with the greedy pick — and the same argument applies to whatever is left.

Why earliest start fails. Starting early says nothing about ending early. D starts at 0 and holds the hall until 10, which blocks all four of the other events — A, B and C all start before 10, and so does E. One greedy pick buys 1 event where finish time buys 3. The rule optimises a quantity that has no bearing on how much room is left afterwards.

Why shortest duration fails. A short event can sit exactly across the boundary between two longer ones. B runs 4–6, only two hours, and taking it kills both A (1–5) and C (5–9). Length is not what blocks other events; the finishing time is.

Cost. Sorting dominates at O(n log n); the pass itself is O(n) and holds one extra number, so it is O(1) beyond the taken list it returns. If the events already arrive sorted by finish time, the whole thing is one linear scan.

05 Where greedy fails

Coins 1, 3 and 4 — make exactly 6

Same shape of rule: at every step take the biggest coin that still fits. Greedy takes the 4 first, because 4 is the largest coin not bigger than 6. That leaves 2, and 2 can only be paid with two 1-coins. The answer is 4 + 1 + 1, three coins. The best answer is 3 + 3, two coins, and greedy can never reach it because it never considers passing up the 4.

Dynamic programming refuses to commit. It works out the cheapest way to make every amount from 1 up to 6, so by the time it reaches 6 it can compare "a 1 on top of the best answer for 5", "a 3 on top of the best answer for 3" and "a 4 on top of the best answer for 2", and keep the smallest.

amount0123456
min coins0121122dp[6] = dp[3] + 1
coin used113443follow 6 → 3 → 0 · 3 + 3
greedy0121123greedy matches dp everywhere except 6

Greedy — biggest coin first

sort coins descendingcount = 0while amount > 0:    c = largest coin <= amount   # none? give up    amount = amount − c;  count = count + 1return count

DP — best answer for every amount

dp[0] = 0for a in 1..target:    dp[a] = INF    for c in coins:        if c <= a:            dp[a] = min(dp[a], dp[a − c] + 1)return dp[target]

What greedy is actually assuming. Taking the 4 is a bet that some best answer for 6 contains a 4. It does not. That bet is safe only in a canonical coin system, where the largest-coin-first rule happens to be optimal at every amount. Indian 1, 2, 5, 10, 20, 50 and US 1, 5, 10, 25 are canonical, so greedy is safe on real money. 1, 3, 4 is not, and nothing in the code notices.

What DP does differently. It never commits, so it pays for that with a table. Cost is O(target × number of coins) time and O(target) space, against greedy's O(n log n) sort plus a handful of subtractions. On amount 6 with three coins the inner loop runs 6 × 3 = 18 times, against greedy's 3 coin picks — and DP is the only one of the two that is correct.

Greedy can also get stuck. Drop the 1-coin and use only 3 and 4. Greedy takes the 4, is left with 2, finds no coin that small, and reports that 6 cannot be paid. 3 + 3 pays it exactly. A wrong answer is bad; a confident "impossible" is worse.

Greedy always returns something. Nothing in the code notices that it is the wrong thing — which is exactly what makes this failure mode dangerous in an interview and in production.

06 Cheat sheet

Which greedy rules are actually proved

ProblemGreedy keyVerdict
Activity selectionearliest finish timeO(n log n) correct · exchange argument
Fractional knapsackhighest value ÷ weightO(n log n) correct · the last item can be cut
0/1 knapsackhighest value ÷ weightwrong · needs DP, O(nW)
Coin change, canonical coins (1 2 5 10)largest coin that fitsO(n log n) correct · true for real currency
Coin change, arbitrary coins (1 3 4)largest coin that fitswrong · needs DP, O(target × n)
Huffman codingmerge the two smallest frequenciesO(n log n) correct · with a min-heap
Minimum spanning tree (Kruskal)cheapest edge joining two componentsO(E log E) correct · cut property
Shortest path with a negative edgesettle the nearest node (Dijkstra)wrong · needs Bellman–Ford, O(VE)
Greedy-choice propertyThe thing you must prove: some optimal answer contains your first pick. No proof, no guarantee — and the code will not tell you.
Optimal substructureAfter the pick, the rest is the same problem on smaller input. DP needs this too; only greedy also needs the choice property.
Disproof in 30 secondsFind one small input where the locally best move blocks two better ones. One counterexample kills a greedy rule; a thousand passing tests prove nothing.

07 Where & why

When to reach for greedy, and what to reach for instead

Greedy is the cheapest correct algorithm when it is correct — a sort and one pass, O(n log n), against DP's table at O(n × W) time and memory. The entire decision is whether you can defend the choice rule, and in an interview you will be asked to defend it out loud.

Reach for it when…

You can state the exchange argumentIn one sentence, why swapping your first pick into any optimal answer keeps it optimal. For activity selection: the earliest-finishing event frees the hall soonest, so it can replace whatever the optimal schedule started with.
The items are divisibleFractional knapsack works because you can take 20 kg of a 30 kg item, so the sack ends exactly full. Make the items indivisible and the same value-per-weight rule breaks.
It is a proved classicMinimum spanning tree, Huffman coding, interval scheduling, Dijkstra on non-negative weights. Cite the proof rather than reinventing it, and say which proof you are citing.
You need a fast bound, not the exact answerEven when greedy is not optimal it usually lands close, which is enough for a scheduling heuristic or for the upper bound inside a branch-and-bound search.

Use something else when…

SituationBetter choiceWhy
Taking one item changes what the remaining items are worthDynamic programmingDP keeps every sub-answer instead of committing. 0/1 knapsack is exactly this shape, at O(nW).
Coin denominations you do not controlDP over amountsGreedy is only correct on canonical systems. 1, 3, 4 on amount 6 already breaks it, and 3, 4 makes it give up entirely.
You need every optimal answer, or a count of themDP or backtrackingGreedy walks one path and cannot enumerate the others, because it has thrown them away.
Edge weights can be negativeBellman–FordDijkstra settles the nearest node and never revisits it; a negative edge can make a settled distance shrink later.
Interviewers rarely ask "is this greedy?". They ask "why is that correct?" — and the answer they want is either the exchange argument in one sentence, or an honest "I cannot prove it, so I would use DP".

08 Interview questions

What interviewers actually ask

Ten questions in the order an interview escalates: what greedy is, why finish time works, the coin-change counterexample that decides the round, and then how you tell the two apart on a problem you have never seen.

What does it mean for an algorithm to be greedy?
It commits to the choice that looks best right now and never revisits it. There is no backtracking and no table of alternatives, which is why greedy runs fast — but also why it can walk confidently into a worse answer.
Why does sorting by finish time work for activity selection?
Finishing earliest leaves the most room behind it. The exchange argument makes it rigorous: take any optimal schedule, and swapping its first event for the earliest-finishing one never causes a clash and never shortens the schedule, so an optimal answer exists that starts with the greedy pick.
Give a case where a greedy choice is provably wrong.
Coin change on denominations 1, 3 and 4 making 6. Greedy grabs 4, then needs 1 and 1, so three coins. The optimal answer is 3 and 3, two coins. The largest coin that fits is simply not always part of the best solution.
When IS greedy coin change correct?
Only for canonical systems, which most real currencies are. There is no shortcut for recognising one by eye — you either prove it for the system or you run dynamic programming. In an interview, say the denominations matter rather than claiming greedy always works.
How do you decide whether greedy applies to a new problem?
Look for the greedy-choice property (a locally best pick is contained in some optimal solution) and optimal substructure (the rest of the problem is the same problem, smaller). If you cannot argue the first one, assume greedy is wrong and reach for dynamic programming.
What is the exchange argument?
The standard proof technique for greedy. You take an arbitrary optimal solution, show you can swap in the greedy choice without making it worse, and repeat — so a greedy solution is at least as good as the optimal one. If the swap step fails, that failure usually hands you your counterexample.
What is the complexity of activity selection?
O(n log n), dominated entirely by the sort. The scan afterwards is a single O(n) pass that touches each event once. If the input arrives pre-sorted by finish time, it drops to O(n).
Greedy versus dynamic programming — how do you choose?
Greedy is a sort plus one pass with O(1) extra state; DP explores overlapping subproblems and pays a table of memory for it. Try to break greedy with a small counterexample first: if you cannot after genuinely trying, argue the exchange property. If you can, you have just discovered the problem needs DP.
Is fractional knapsack greedy, and is 0/1 knapsack?
Fractional is greedy and provably optimal — sort by value per unit weight and fill, splitting the last item. 0/1 is not: you cannot split, so a high-ratio item may crowd out two items that together beat it. That one needs DP.
Why is sorting by shortest duration a tempting but wrong key?
It feels efficient, as though short events waste less room. But a short event sitting across the middle of the timeline can block two longer events on either side of it, so it loses count. On the lesson’s five events it keeps only B and E, two, where the finish-time key keeps three.

09 Practice problems

Six problems, in order of bite

Every one is solvable with what is above: a sort followed by one committed pass, the exchange argument that says whether that pass is safe, and the DP you fall back on when it is not.

Lemonade change

Easy
Customers queue paying with 5, 10 or 20 rupee notes for a 5 rupee drink. You start with nothing and must give correct change to everyone in order, or return false the moment you cannot.
Follow-up
There is only ever one sensible way to hand back change, so the greedy pick is forced rather than chosen — the difficulty is spotting which note to part with when you could pay 15 two different ways.
Show the hint
You never need to search: track how many 5s and 10s you hold, and when both would work, ask which note is harder to replace later.

Assign the biscuits

Easy
Each child has a greed factor and each biscuit a size; a child is satisfied when the biscuit is at least their factor. Return how many children you can satisfy at most.
Follow-up
Two lists must be sorted, not one, and the pairing is decided by walking both at once rather than by picking a best-first from either.
Show the hint
Sort both lists first, then ask what the smallest remaining biscuit is good for: if it cannot satisfy the least greedy child still waiting, nothing else about it matters.

Jump to the end

Medium
Each cell of an array holds the maximum number of steps you may jump forward from it. Return the fewest jumps needed to reach the last cell, assuming it is always reachable.
Follow-up
The greedy quantity is not the biggest jump available — committing to that is wrong. Something else has to be tracked across the whole current jump before you spend the next one.
Show the hint
Walk left to right carrying the furthest cell anything you have already stood on could reach, and work out the exact moment you are forced to spend a jump rather than choosing to.

Two machines, minimum wait

Medium
You have a list of job durations and two identical machines. Assign every job so that the moment the last job finishes is as early as possible, and return that finishing time.
Follow-up
The obvious greedy key survives every set of four jobs you can build, which is exactly why it feels proved. Five jobs is the smallest size where it loses — find such a set, then decide whether any greedy key survives.
Show the hint
Try longest-first onto whichever machine is free soonest, then hunt for a counterexample with three equal small jobs and two larger ones. Whether you find one tells you which of the two tools in this lesson the problem actually needs.

Refuel the fewest times

Medium
Driving a fixed distance with a starting fuel amount, you pass stations each offering a top-up. Return the fewest stops needed to finish, or minus one if you cannot.
Follow-up
On arrival at a station you cannot yet tell whether stopping is needed — that depends on road you have not driven. Every greedy rule that decides on the spot is wrong on some input; find one before you write any code.
Show the hint
Nothing says you must decide at the station. Keep every station you have driven past in a structure that can hand back the best of them on demand, and consult it only at the instant you would otherwise run dry.

Hand out the sweets

Hard
Children stand in a line with a rating each. Every child gets at least one sweet, and any child rated higher than an immediate neighbour must get more sweets than that neighbour. Return the smallest total.
Follow-up
One left-to-right pass satisfies half the constraints and quietly breaks the other half, and no single pass in either direction fixes both.
Show the hint
Make one pass in each direction keeping a separate count, then combine the two counts per child in the way that satisfies both neighbours at once.