bigonotes
DSA: Patterns & PythonGreedy

Greedy Overview & Exchange Argument

Recognize when a locally best choice is globally best, prove it with an exchange argument, and know when to fall back to DP.

6 min readLast updated

A greedy algorithm makes the best-looking choice at each step and never revisits it. When it works, it's usually the simplest and fastest solution, often a sort plus one pass. The hard part isn't the code but knowing that the local choice is safe. That's tricky, because greedy problems often look a lot like DP problems.

The idea#

Greedy works when a locally optimal choice leads to a globally optimal answer. The test is: if the current choice doesn't affect which future choices are available (or only leaves more of them), greedy works. If it does, you need DP to explore the options.

Three signs point to greedy:

  1. Making the locally best choice at each step gives the global best.
  2. You never need to undo or revisit an earlier decision.
  3. There's often a sort first, then a single pass in that order.

If you can't convince yourself of these, or you find a counterexample, use DP.

How to spot it#

Ask these questions before choosing greedy over DP:

Question If yes
Can one locally best choice always reach the global best? Greedy is likely
Does sorting by some key make the best next choice obvious? Sort, then greedy
Does a choice now remove options you might need later? DP is safer
Would you need to track several candidate answers at once? DP
Is the problem "max subarray"-like, with extend-or-restart? Kadane (it's both greedy and DP)

The usual shape#

Most greedy solutions share one shape: sort by the key that makes the next choice obvious, then make that choice in one pass. The sort costs O(nlog⁡n)O(n \log n) and the pass costs O(n)O(n).

sort items by the key that makes the best next choice obvious
state = starting value                 # last chosen end, count, total
for each item in sorted order:
    if item is compatible with state:  # the locally best move is safe
        take it and update state
    else:
        skip it, or start a new group
return the answer built from state

The code is the easy part. The real work is choosing the key and proving that the local choice is safe, which the next section covers.

Proving it: the exchange argument#

The standard proof asks: "if you sort and pick this way, can anything else be better?" Take any optimal answer that differs from the greedy one. Find the first place they differ and swap the optimal answer's choice for the greedy choice. If the result is still valid and no worse, you can repeat until the optimal answer is the greedy one, so greedy is optimal too.

With intervals, the swap often moves a choice to the earliest end. No interval ends earlier, so the moved choice misses nothing, and it leaves the most room on the number line for everything after it.

Two other proof styles come up often:

  • Edges are extremes: in a sorted array, the smallest and largest values are guaranteed to be at the ends, which lets you argue that pairing them one way can't be beaten (see Sort, Then Place or Pair).
  • Telescoping sums and bounds: split the target into pieces you can reason about one by one, or show the answer can't exceed a bound and then reach it (see Counting & Bound Arguments).

When DP was the first idea#

Sometimes DP is the first idea that comes to mind, and a DP solution even works, but it's too slow. A DP over "last index and some state" can time out where one pass over the input would do. Look for what matters to the answer and drop everything else. Wiggle Subsequence in the hints below is the classic case.

Maximum Number of Weeks (LC 1953) is another example: a heap simulation works, and a counting argument then replaces it with a closed form.

When greedy fails#

The opposite mistake is trusting greedy when a choice does close off options. These two are the classic cases.

  • Coin change with arbitrary coins. With coins [1, 3, 4] and amount 6, "largest coin first" gives 4 + 1 + 1 (3 coins); 3 + 3 uses 2. Greedy is only safe for canonical systems such as [1, 5, 10, 25].
  • 0/1 knapsack. Greedy by value per weight is optimal only when items can be split (fractional knapsack). With whole items, use DP.

Where other greedy patterns live#

Greedy shows up in other parts of the site too. These patterns have their own notes.

Pattern Where
Interval scheduling (sort by end, earliest finish) Intervals: Choose the Sort Key
Kadane's extend-or-restart, circular max subarray Kadane & Running-State DP
Heap greedy and greedy with regret (ladders and bricks, refueling) Scheduling & Greedy with Regret
Monotonic stack greedy (remove K digits, smallest subsequence) Monotonic Stack for Greedy Choices
Minimum spanning trees (Kruskal, Prim) Minimum Spanning Tree

Pitfalls#

These mistakes cause the most wrong answers.

  • Trusting a greedy rule because it passes the examples. Try to break it with a small counterexample first.
  • Sorting by the wrong key. For interval problems, start vs end time decides whether the greedy is correct.
  • Reaching for DP on a problem with an obvious one-pass greedy, then getting a time limit.

Problems in this pattern#

These problems use this pattern. Solve them before you open the hints below. Start with the first one.

Hints#

Open a hint only after you have tried the problem for a while.

Hint: Minimum Number of Arrows to Burst Balloons

Sort balloons by end. The first balloon must be hit by some arrow, and shooting at its end also hits every balloon that starts at or before that point. Start a new arrow only when a balloon starts after the last shot.

Hint: Non-overlapping Intervals

Sort by end and keep the interval that finishes earliest; it leaves the most room for the rest. Count the intervals you have to skip. See Intervals: Choose the Sort Key.

Hint: Wiggle Subsequence

A DP over "last index and last direction" works but times out. Only the ups and downs matter: equal neighbors change nothing, and every change of direction can be part of the answer. So count the changes of sign between neighbors.

Hint: Maximum Subarray

This is Kadane's extend-or-restart: either extend the current run or start a new one at this element. It is both greedy and DP. See Kadane & Running-State DP.

Hint: Jump Game

Keep one number: the furthest index you can reach. If you stand past it, you are stuck. See Reachability.

Hint: Maximum Units on a Truck

Sort box types by units per box, highest first, and take as many of each as still fit. Swapping any box for a lower-unit one can only lose units, which is the exchange argument.

Key takeaways#

  • Greedy works when the current choice doesn't restrict future choices.
  • Sort first, then make the obvious choice in one pass.
  • Prove it with an exchange argument: swapping in the greedy choice never hurts.
  • When a choice removes options you might need, use DP.
  • Test a greedy rule against a small counterexample before trusting it.
Revision notes · 2 min

Core idea#

A greedy algorithm makes the best-looking choice at each step and never revisits it. It works when the current choice does not restrict future choices. If it does, use DP.

Key points#

  • Signs of greedy: the local best gives the global best, no decision needs undoing, and a sort comes first.
  • Prove it with an exchange argument. Swap the greedy choice into an optimal answer at the first difference. If it stays valid and no worse, greedy is optimal.
  • Other proofs: the extremes sit at the ends of a sorted array, or a bound that a construction reaches.
  • Minimum Arrows: sort by end and shoot at the earliest end. That leaves the most room for the rest.
  • Some DP solutions are correct but too slow. Wiggle Subsequence counts sign changes instead.
  • Test a greedy rule against a small counterexample before you trust it.

Greedy or DP#

Question If yes
Does sorting make the best next choice obvious? Sort, then greedy
Does a choice now remove options you need later? DP
Must you track several candidate answers? DP
Extend-or-restart, like max subarray? Kadane (greedy and DP)

Greedy fails on coin change with arbitrary coins: [1, 3, 4] for 6 gives 3 coins, but 3 + 3 uses 2. It also fails on 0/1 knapsack; only fractional knapsack is greedy.

Template#

sort items by the key that makes the best next choice obvious
state = starting value                 # last chosen end, count, total
for each item in sorted order:
    if item is compatible with state:  # the locally best move is safe
        take it and update state
    else:
        skip it, or start a new group
return the answer built from state

Pitfalls#

  • Trusting a rule because it passes the examples.
  • Sorting by the wrong key. Start versus end decides correctness in interval problems.
  • Using DP when a one-pass greedy exists, then getting a time limit.

Say it in an interview#

  • "This choice never removes a future option, so greedy is safe."
  • "Exchange argument: swapping the greedy choice into any optimal answer never makes it worse."

Found a mistake? Email ask@bigonotes.com