Coding Interview Patterns

Course Content

Coding Interview Patterns

20 sections · 146 lessons

Greedy: The Core Idea


A cashier owes you 68 cents and has coins of 25, 10, 5 and 1. Without thinking, she takes the biggest coin that fits, again and again: 25, 25, 10, 5, 1, 1, 1. Seven coins, and no other way uses fewer. She never went back to reconsider a coin she had already handed over.

That habit is a greedy algorithm: build the answer one step at a time, take the option that looks best right now, and never undo it. The code is usually a sort and a loop. The difficulty is knowing whether the habit is allowed — because on a different set of coins, the same cashier gives the wrong answer and nothing warns her.

This lesson is mostly about that second part. Writing greedy code takes a minute. Earning the right to write it — by proving the choice is safe or breaking it with a small input — is what interviewers test.

The code is never the hard partIt looks greedy when• Maximise or minimise over choices• One local rule is obviously appealing• Sorting makes the order obvious• The answer is a count, not a setWhat is actually tested• Can any optimal answer be exchanged?• Have you hunted for a counterexample?• Would DP be needed instead?• The argument, not the four lines
Writing the greedy loop takes a minute; earning the right to write it is the whole interview.

The picture: a cashier with a strange till

Here is the cashier's habit as code, next to the version that explores every option.

Python
def coin_change_greedy(coins: list[int], amount: int) -> int:    """Largest coin first. Fast, and wrong for some coin sets."""    used = 0    for coin in sorted(coins, reverse=True):        take = amount // coin          # as many of this coin as fit        used += take        amount -= take * coin    return used if amount == 0 else -1def coin_change_dp(coins: list[int], amount: int) -> int:    """Fewest coins, by trying every last coin for every smaller amount."""    INF = amount + 1    best = [0] + [INF] * amount    for value in range(1, amount + 1):        for coin in coins:            if coin <= value:                best[value] = min(best[value], best[value - coin] + 1)    return best[amount] if best[amount] != INF else -1

Trace the greedy version on coins [1, 3, 4] and amount 6:

stepremaininglargest coin that fitscoins used
1641
2212
3113

Greedy returns 3. The dynamic programming (DP) version returns 2 (3 + 3). At amount 6, DP compares "use a 4 and solve 2" with "use a 3 and solve 3" and keeps the better one. Greedy never looks at the second branch, because 3 is not the largest coin. Run both on every amount up to 30 and greedy is wrong on 6, 10, 14, 18, 22, 26 and 30 — and silent every time.

Make 30 from coins [25, 10, 1]Greedy: always take the largest coin that fitstake 25remaining 525 too big; 10 too big; take 1remaining 4take 1 ×4remaining 06 coins: 25 + 1+1+1+1+1Optimal: ignore the biggest cointake 10remaining 20take 10remaining 10take 10remaining 03 coins: 10 + 10 + 10Greedy took the locally best coin at every step and still lost by three coins.Greedy needs a proof, not a hunch: with coins [25, 10, 1] no exchange argument holds, so this problem is dynamic programming. With [25, 10, 5, 1] — real currency —greedy happens to be correct, which is exactly what makes the failure hard to spot.
The coin set decides whether greedy is correct — which is why "it worked on my example" is not an argument.

This is the whole difference between the two techniques. Greedy commits; DP explores.

greedydynamic programming
at each steptakes one option, for goodevaluates every option
typical costO(n) or O(n log n)O(states × work per state)
correctnessmust be argued for each problemfollows from the recurrence
when wrongreturns a plausible, slightly wrong numbertoo slow, or runs out of memory

The last row is the dangerous one. A wrong DP usually crashes or times out. A wrong greedy returns a number, confidently, and you only find out when an interviewer hands you [1, 3, 4].

How to recognise it

Interviewers never say "use greedy". These are the signals:

  • An optimisation with an obvious one-line rule. "Take the one that finishes first." "Take the biggest." "Go as far as you can." If a plausible rule jumps out, greedy is a candidate — and a suspect.
  • Scheduling, covering or allocating. Meetings, arrows through balloons, fuel stops, cookies to children. This family has more correct greedy solutions than any other.
  • The answer is a yes/no or a count, and a single pass feels enough. "Can you reach the end?" "What is the fewest number of…?"
  • The constraints rule out DP. With n up to 10⁵ or 10⁶, an O(n²) DP is 10¹⁰ steps or more. If the answer must be O(n) or O(n log n), you cannot search the choices, so you must commit to them.

One test separates the problems where greedy is safe from the ones where it is not: does taking the locally best option ever close a door that a worse option would have left open? Taking the 4 left an awkward remainder of 2 — a door closed. Taking the earliest-finishing meeting leaves the most time for the rest — nothing closed.

How it works: the exchange argument

The exchange argument has three steps, and you can say it without any formal notation:

  1. Take any optimal answer — one that does not make the greedy choice.
  2. Swap the greedy choice in — replace the optimal answer's first differing choice with the greedy one.
  3. Show nothing got worse — the result is still valid and just as good, so an optimal answer containing the greedy choice exists. Repeat for the next choice.

Apply it to interval scheduling: given meetings with start and end times, keep as many as possible with no two overlapping (a meeting may start exactly when another ends).

Three rules suggest themselves. Two die in seconds:

  • Earliest start first. Meetings [1, 10], [2, 3], [4, 5], [6, 7]. The rule keeps [1, 10] and nothing else fits: 1 meeting. The three short ones fit together: 3.
  • Shortest first. Meetings [1, 5], [4, 7], [6, 10]. The shortest is [4, 7], and it overlaps both others: 1 meeting. [1, 5] and [6, 10] fit together: 2.
  • Earliest end first. Survives both inputs. Now prove it.

Let g be the meeting that ends earliest of all. Take any optimal schedule and let o be its first meeting. If o is g, done. Otherwise g ends no later than o — that is what "ends earliest" means. Every other meeting in the optimal schedule starts after o ends, so it also starts after g ends. Swap o for g: the schedule is still valid and has the same size, so it is still optimal. Repeat the argument on the meetings left after g, and greedy's whole answer is optimal.

The exchange argument in four stepsTake anyoptimal answerFind thefirst differenceSwap thegreedy choice inShow it isno worseIf the swap never hurts, repeating it turns any optimum into the greedy answer.
You never prove greedy is best; you prove nothing is lost by preferring it.

The intuition behind the proof: the greedy choice is the one that constrains the future least. Ending earliest leaves the most room for everything after it. Use that sentence to find a candidate rule — but not to prove it. "Largest coin" also sounds like it constrains the future least (it leaves the smallest remainder), and it is wrong, because a smaller remainder is not always an easier one. Candidate from intuition, confirmation from the exchange argument, death by counter-example.

Variants

Almost every greedy interview problem takes one of these forms. Each problem lesson in this section is one row.

formthe greedy ruleproblems
one running valuekeep the furthest reach, the tank level or the piece end; never look backJump Game, Gas Station, Partition Labels
levels of reachcount the jumps as ranges, like breadth-first search without a queueJump Game II
sort, then sweepsort by the end, keep or shoot when the next item does not fitMinimum Number of Arrows to Burst Balloons, Non-overlapping Intervals
smallest sufficient matchsort both sides, give each need the smallest thing that meets itAssign Cookies (below)
one pass per directionsatisfy the left rule, then the right rule, keep the largerCandy
count the bottleneckthe most frequent item fixes the shape of the answerTask Scheduler (below)
best available so farpush options into a heap as they become reachable, take the bestRefuelling Stops, IPO — see Heaps

The bottleneck form needs no loop at all. In Task Scheduler, equal tasks must be at least n slots apart and you want the shortest schedule. The busiest task decides the shape: if it appears top times, it needs top − 1 blocks of n + 1 slots, then one final slot for each task tied at top. Tasks A A A A B B C with n = 2 give 3 × 3 + 1 = 10 slots, for example A B C A B _ A _ _ A. If there are more tasks than that frame, the gaps fill with real work and the answer is simply the number of tasks. The Heaps section works this problem in full, with the heap simulation and this formula side by side.

The templates

Template 1 — sort, then sweep. This is interval scheduling, the most reused greedy in interviews.

Python
def max_non_overlapping(intervals: list[tuple[int, int]]) -> int:    """Most intervals that can be kept with no two overlapping (touching is fine)."""    kept = 0    last_end = float("-inf")    for start, end in sorted(intervals, key=lambda iv: iv[1]):   # earliest end first        if start >= last_end:          # fits after the last interval we kept            kept += 1            last_end = end    return kept
  • sorted(..., key=lambda iv: iv[1]) is the whole algorithm. Sort by the end, because that is the choice the exchange argument proved safe.
  • last_end = float("-inf") means "nothing kept yet". Never start it at 0 or −1: coordinates can be negative.
  • start >= last_end decides whether touching counts as overlapping. Here it does not. Ask the interviewer; it changes one character.

Template 2 — smallest sufficient match. In Assign Cookies, each child has a greed value, each cookie has a size, a child is happy with a cookie at least as big as their greed, and each child gets at most one cookie. Make as many children happy as possible.

Python
def feed_children(greed: list[int], sizes: list[int]) -> int:    """Most children satisfied; each child gets at most one cookie."""    greed, sizes = sorted(greed), sorted(sizes)    child = 0                          # the least greedy child still hungry    for size in sizes:                 # smallest cookie first        if child < len(greed) and size >= greed[child]:            child += 1                 # smallest cookie that satisfies this child    return child

Greed [4, 1, 2] and cookies [3, 1, 1] sort to [1, 2, 4] and [1, 1, 3]. Cookie 1 feeds the child with greed 1. The second cookie 1 is too small for greed 2 and is skipped. Cookie 3 feeds greed 2. Answer 2. The exchange argument in one sentence: if an optimal answer gives the least greedy child a bigger cookie, swap in the smallest cookie that fits — the bigger one is at least as useful to everyone else.

Complexity

Most greedy solutions are O(n log n) time because of one sort, then O(n) for the sweep. The running-value forms (Jump Game, Gas Station) need no sort and are O(n). Space is O(1) beyond the input, or O(n) if you sort a copy, as Python's sorted does.

If your "greedy" solution is O(n²), stop. It is probably a DP in disguise, and you should decide which one you are writing.

Where it goes wrong

1. Not checking. This is the costly one, because it loses the whole question, not a few minutes. A wrong greedy returns a believable number. The fix is a habit: before writing the loop, spend sixty seconds trying to break your rule with three or four elements and an awkward gap. [1, 3, 4] is the template for anything with denominations or weights.

2. Sorting by the wrong key. The sweep is right; the order it sees is wrong. Interval scheduling sorted by start keeps a long early meeting that blocks several short ones. Merging intervals sorted by end misses overlaps. The rule of thumb: when you are selecting or covering, sort by end; when you are merging, sort by start.

3. Problems that look greedy but need DP. A local choice that changes the shape of what is left — not only its size — needs DP.

looks greedyactually needsthe tell
Coin ChangeDPa remainder can be awkward for the coin set
0/1 KnapsackDPone heavy valuable item can crowd out two better ones
Partition Equal Subset SumDPthe target is exact, not a maximum
Jump Gamegreedyreachable indices form one block; nothing is crowded out
Interval Schedulinggreedyending earliest survives the exchange argument

4. Boundaries. Whether two ranges that touch overlap is a question, not an assumption. It is the difference between > and >= in every sort-and-sweep problem.

Before you write greedy code, run this diagnostic in order: state the rule in one sentence; try to break it in sixty seconds; say the exchange argument; check whether the constraints allow DP.

The diagnostic, in orderState thegreedy ruleHunt acounterexampleTry theexchangeargumentOtherwise,use DPCoin Change with coins 1, 3 and 4 defeats the obvious take-the-largest rule.
A wrong greedy answer is wrong quietly, so the counterexample hunt comes before any code.

Check your understanding

0 of 3 answered

1.You want to keep the most non-overlapping meetings. Which sort key makes the greedy sweep correct?

2.Coins are 1, 3 and 4. What does "largest coin first" return for amount 6, and what is optimal?

3.What does an exchange argument show?