One conference room, six meeting requests, and you want to fit as many as possible. There's an algorithm that solves this in one pass with no table, no recursion, and no second thoughts: sort by end time, then take every meeting that starts after the last one you took. That's a greedy algorithm: make the locally best choice at every step, commit to it, and never reconsider. No backtracking, no caching, just a rule applied left to right.
The speed comes from the commitment, and so does the danger. A greedy that never revisits a choice can lock itself into a bad one, so every greedy algorithm carries a proof obligation: the local rule must provably survive to a global optimum. Sometimes it does, and you get the fastest solution in the room. Sometimes it doesn't, and the failure is quiet enough to pass half the test cases first.
Watch the meeting scheduler work. Six requests, written as (start, end): (1, 2), (3, 4), (0, 6), (5, 7), (8, 9), (5, 9). Sorted by end time, the sweep takes (1, 2), takes (3, 4), skips (0, 6) (it started before 4), takes (5, 7), takes (8, 9), and skips (5, 9). Four meetings, and no schedule does better. The rule works because the meeting that ends earliest frees the most future: anything a longer meeting could enable, the earlier-ending one enables too.
Now predict what happens with the friendlier-sounding rule, earliest start time first, on the requests (1, 10), (2, 3), (3, 4). The answer is one meeting: it grabs (1, 10), which blocks both of the short ones, while the end-time rule fits two. Same sweep, same data, opposite outcome, and that's the lesson that generalizes: in a greedy algorithm, the sort key carries the entire proof. The formal version of "the proof" is called an exchange argument: take any optimal schedule that differs from the greedy one, swap its first differing pick for the greedy pick, and the schedule never gets worse, so the greedy result must be optimal too.
The cleanest greedy problems don't even need the sort. Jump Game hands you [2, 3, 1, 1, 4], where each value is the farthest you can jump from that index, and asks whether the last index is reachable. The greedy insight: the only fact worth carrying is the farthest index reachable so far. Walk the array once. At index 0 the reach becomes 2, at index 1 it stretches to 4, and since the last index is 4, the answer is already yes. On [3, 2, 1, 0, 4] the reach gets stuck at 3, index 4 sits beyond it, and the walk reports false: that 0 at index 3 is a hole every path falls into.
def can_jump(nums: list[int]) -> bool:
# The farthest index reachable so far
farthest = 0
for i, jump in enumerate(nums):
# A gap the previous jumps can't cross
if i > farthest:
return False
# Greedy choice: remember the best reach seen
farthest = max(farthest, i + jump)
return TrueKadane's algorithm runs the same shape on a different question: the maximum subarray sum. At each element the choice is binary, extend the run you're carrying or abandon it and start fresh, and current = max(num, current + num) decides in constant time. On [-2, 1, -3, 4, -1, 2, 1, -5, 4] the best run is [4, -1, 2, 1] for a sum of 6, found in one pass.
def max_subarray(nums: list[int]) -> int:
# Best sum ending here, and best sum seen anywhere
current = nums[0]
best = nums[0]
for num in nums[1:]:
# Greedy choice: extend the run, or start fresh here
current = max(num, current + num)
best = max(best, current)
return bestYou might think an algorithm this natural fails loudly when it fails. It fails silently. Make change for 6 with coins worth [1, 3, 4]: the greedy rule "take the biggest coin that fits" grabs the 4, then two 1 coins, three total. The right answer is 3 + 3, two coins, and greedy can never find it because the optimal solution doesn't contain the biggest coin at all. The same rule works perfectly on US currency, because [1, 5, 10, 25] belongs to the family of canonical coin systems where the greedy choice happens to be provably safe. Nothing about the rule announces which kind of system you're holding.
That's the trap in interviews too: a greedy that passes the visible examples has proven nothing, since examples are friendly by design. The honest workflow is to hunt for a counterexample on purpose, and if a real one turns up, hand the problem to dynamic programming, which weighs every choice instead of committing to one.
Greedy solutions are short. The discipline around them is the real procedure:
O(n log n) for the sort or O(n) without it.heap keeps it on top for O(log n) per update. Static rules sort once. Moving targets need the heap.Four families cover most greedy interview problems. Reach extension carries a single furthest-point variable (Jump Game, Jump Game II, Gas Station with its reset trick). Sort-then-sweep pairs a carefully chosen key with one pass (meeting scheduling, Assign Cookies, Partition Labels cutting at the farthest last occurrence). Extend-or-restart keeps a running candidate and abandons it when it goes negative (Kadane). And frequency math turns scheduling into arithmetic: Task Scheduler's answer is just (maxFreq - 1) * (n + 1) plus the tasks tied at the top, because the most frequent task dictates the skeleton everything else fills.
The recognition signal is the phrase "maximum number of" or "minimum number of" paired with a feeling that a simple rule should work. Treat the feeling as a hypothesis. The four families above all come with proofs, and that's exactly what separates them from the coin trap.
Greedy rewards skepticism about your own cleverness. Five habits that keep it honest:
Check yourself first: why does sorting meetings by start time fail where end time succeeds, and what two-coin answer does greedy miss for coins [1, 3, 4] and amount 6? With those two cold, the problems below become exercises in naming the rule and testing it. Start with the reach extenders, then the sort-and-sweeps:
Completed | Title | Status | Notes | Solution | |
|---|---|---|---|---|---|
Medium | |||||
Medium | |||||
Medium | |||||
Medium | |||||
Medium | |||||
Medium | |||||
Medium | |||||
Medium |