When greedy works (exchange argument)
In one line
A greedy algorithm makes the locally best choice at every step and never looks back; it is correct only when you can prove that some optimal answer agrees with that choice.
What it is
A greedy algorithm builds the answer one decision at a time, always taking the option that looks best right now (the smallest, the earliest, the cheapest) and never undoing it. There is no backtracking and no table of subproblems, so greedy solutions are usually short and fast, often O(n log n) because of one sort.
The catch is that 'looks best now' is not always best overall. Greedy is correct only for problems with two properties: the greedy-choice property (some optimal solution starts with the greedy choice) and optimal substructure (after making that choice, what is left is a smaller instance of the same problem).
The standard way to prove the greedy-choice property is the exchange argument: take any optimal solution, swap one of its choices for the greedy choice, and show the result is still valid and no worse. If that swap always works, greedy is optimal.
Why interviewers ask
Interviewers like greedy because the code is easy but the reasoning is not. They want to see you pick the right greedy rule (sort by end time, not start time), justify it in a sentence or two, and recognise when greedy fails so you switch to dynamic programming. Offering a counterexample to a tempting greedy rule is a strong signal.
How it works
- 1Find the decision you make repeatedly: which item to take next, which interval to keep, which child gets the next cookie.
- 2Propose a rule for that decision, usually 'sort by some key and take the first that fits'. Typical keys are smallest size, earliest end time, largest ratio, or farthest reach.
- 3Try to break the rule with small counterexamples (2 to 4 items). If you find one, the rule is wrong; try another key or switch to DP.
- 4Prove it with an exchange argument: take an optimal solution that differs from greedy at the first step, swap in the greedy choice, and show it is still feasible and not worse. By induction, greedy matches an optimal answer at every step.
- 5Implement: sort (O(n log n)) or use a heap, then one linear pass that keeps a little state (count, last end, current reach).
Worked example · Coin change: where greedy works and where it breaks
- Coins [1, 5, 10, 25], amount 63. Largest first: 25 (38 left), 25 (13 left), 10 (3 left), 1, 1, 1. Total 6 coins, which is optimal. US coins are a 'canonical' system, so greedy always works.
- Coins [1, 3, 4], amount 6. Largest first: 4 (2 left), 1 (1 left), 1 (0 left). Total 3 coins.
- But 3 + 3 = 6 uses only 2 coins. Taking the 4 first was a locally good choice that blocked the best answer, so greedy fails here and coin change in general needs DP in O(amount x coins).
- Now a case where the exchange argument works: children with greed [1, 2, 3] and cookies [1, 1]. Sort both. Give cookie 1 to child with greed 1 (satisfied). The next cookie 1 cannot satisfy greed 2. Answer 1.
- Why giving the smallest adequate cookie to the least greedy child is safe: if an optimal plan gave that child a bigger cookie, swap the two cookies. The least greedy child is still happy, and whoever had the small cookie now has a bigger one, so nobody is worse off.
Picture it
Greedy is like packing for a trip by always grabbing the most useful item still on the bed. For some trips that gives a perfect bag; for others the first bulky item you grabbed leaves no room for two things you needed more. The exchange argument is checking that swapping your first grab for anything else would never have made the bag better.
Reference code
def find_content_children(greed, cookies):
"""Assign cookies: each child i needs a cookie >= greed[i]."""
greed.sort()
cookies.sort()
child = 0
for size in cookies: # smallest cookie first
if child < len(greed) and size >= greed[child]:
child += 1 # least greedy child takes it
return child
def greedy_coins(coins, amount):
"""Largest-coin-first change. Optimal only for canonical systems."""
count = 0
for c in sorted(coins, reverse=True):
count += amount // c
amount %= c
return count if amount == 0 else -1
# find_content_children([1, 2, 3], [1, 1]) -> 1
# greedy_coins([1, 5, 10, 25], 63) -> 6 (optimal)
# greedy_coins([1, 3, 4], 6) -> 3, but 3 + 3 uses 2 coins
find_content_children is a correct greedy proved by the exchange argument, O(n log n) for the sorts. greedy_coins shows the same style of rule failing on a non-canonical coin set, which is why general coin change is solved with DP.
Key patterns
Sort then sweep
When: Items can be ordered by one key and each decision only depends on the last choice
How: Sort by the key that leaves the most room for the future (earliest end, smallest size), then make one pass keeping a small state.
Smallest that fits (matching)
When: Pair two lists, such as cookies and children or boxes and items
How: Sort both and use two pointers; satisfy the least demanding request with the smallest resource that works.
Heap-driven greedy
When: The best choice changes as you go, like always merging or scheduling the current smallest
How: Keep candidates in a min-heap and pop the best each step; O(n log n).
Counterexample check
When: Any time you propose a greedy rule
How: Try 2 to 4 element inputs by hand. If greedy loses to brute force, use DP instead.
Real-world use
Shortest paths and spanning trees
Dijkstra's algorithm (used in routing protocols such as OSPF and in map routing) greedily settles the closest unsettled node. Kruskal's and Prim's algorithms for minimum spanning trees, used in network and circuit layout, greedily add the cheapest safe edge.
Compression
Huffman coding, part of DEFLATE (zip, gzip, PNG) and baseline JPEG, greedily merges the two least frequent symbols, and it is provably optimal among prefix codes.
Cash registers and vending machines
Giving change largest coin first is optimal for canonical coin systems like the US and euro coins, which is why machines can use a simple greedy loop instead of a DP table.
Schedulers
Operating system and job schedulers use greedy rules such as shortest job first, which provably minimises the average waiting time when all job lengths are known up front.
In a project
You are building a free-shipping helper for an online store: given a cart, suggest the fewest extra items that push the order over a 50 dollar threshold, and match warehouse boxes to orders.
- Box assignment is a cookie problem: sort orders by volume and boxes by size, and give each order the smallest box that fits. An exchange argument shows this packs the maximum number of orders.
- For 'fewest extra items to reach the threshold' first try a greedy rule (largest price first) and test it on small carts: it can overshoot badly or miss a cheaper combination.
- Because cost matters, switch that part to a small knapsack-style DP over amounts in cents up to the threshold, which is cheap because the threshold is small.
- Write unit tests that compare the greedy and DP answers against brute force on hundreds of random small carts.
Impact: Box matching runs in O(n log n) and uses fewer large boxes, cutting packaging cost; the suggestion feature is always optimal because you proved greedy wrong for it before shipping. The tests catch any rule change that breaks optimality.
Trade-offs
Use it when
- You can state a sorting key and prove the exchange argument
- The problem has a matroid-like structure (scheduling, spanning trees, Huffman)
- You need near-linear time and DP would be O(n^2) or pseudo-polynomial
Avoid it when
- A small counterexample beats your rule (0/1 knapsack, general coin change)
- Choices interact in ways that a later item can make an earlier choice wrong
- The problem asks to count ways or list all solutions; greedy finds one answer, not all
Cost: Usually O(n log n) for the sort plus O(n) for the pass, with O(1) extra space beyond the sort; heap-based greedy is O(n log n).
Common mistakes
- Assuming greedy works because it passed the given example, without trying a counterexample.
- Choosing the wrong sort key, such as sorting intervals by start time or by length when the problem needs earliest end time.
- Using greedy for 0/1 knapsack; best value-per-weight is only optimal for the fractional version.
- Not being able to explain why the rule is correct; a one-line exchange argument is usually expected.
- Forgetting that sorting changes the indices when the answer must refer to original positions.
Interview questions
Quick revision
- Greedy = take the locally best choice, never undo it.
- Correct only with greedy-choice property + optimal substructure.
- Prove with an exchange argument: swap greedy's pick into an optimal answer, it stays optimal.
- Always test 2 to 4 element counterexamples before coding.
- Coins [1,3,4], amount 6: greedy 3 coins, optimal 2. General coin change needs DP.
- Fractional knapsack is greedy by value/weight; 0/1 knapsack is DP.
- Typical cost: O(n log n) sort + O(n) pass.
Check yourself
1. Which two properties make a greedy algorithm correct?
2. With coins [1, 3, 4], what does largest-coin-first give for amount 6?
3. What does an exchange argument show?
4. Which problem is solved optimally by sorting on value per weight?
Saved on this device. Create a free account to keep it on every device.
Practise this with Sam
A real mock interview, out loud, with an honest scorecard. Free to start.