<style> .reveal-up, .reveal-left, .reveal-right, .reveal-stagger > * { opacity: 1 !important; transform: none !important; } </style>
  1. Learn
  2. DSA
  3. Algorithm design
All lessons in this course

Lesson 39 of 50 14 minAdvanced

Greedy algorithms

Build a solution one locally best choice at a time, learn when that is provably optimal (interval scheduling, coins, fractional knapsack) and when it fails, and how to argue that a greedy rule is correct.

A greedy algorithm builds its answer step by step, and at each step takes the choice that looks best right now, without ever going back. It is usually simple and fast. The hard part is not writing it but knowing whether it is correct: for some problems the locally best choice always leads to the best overall answer, and for others it leads you astray.

You have already met three greedy algorithms: Dijkstra (always finalise the closest place), Prim and Kruskal (always take the cheapest safe edge). Each came with a reason why greedy works there.

Scheduling: which rule is right?

One tower crane, many booking requests, each with a start and end time. Accept as many as possible without overlaps. Three greedy rules sound reasonable: earliest start, shortest booking, earliest finish. Only one is always optimal. Try them:

Interactive

Greedy crane booking

One tower crane, eight requests for it (hours of the day). Accept as many as possible without overlaps. Each rule is greedy: it makes the choice that looks best right now. Which rule is right?

56789101112131415161718A · Rebar deliveryB · FormworkC · Pump set-upD · Steel beamsE · Roof panelsF · Glass crateG · All-day hireH · Generator
Accepted 0 · best possible 4
Order the requests by “earliest finish”: A, C, H, B, D, F, E, G. Take each one that fits, never reconsider.

Earliest finish wins, and we can prove it with an exchange argument: take any optimal schedule. Its first booking ends no earlier than the booking that finishes first overall, so swapping it for that one still leaves a valid schedule of the same size. Repeat for the rest. The greedy choice is never worse than what an optimal answer does.

IntervalScheduling.cs
var bookings = new List<(string Id, int Start, int End)>{    ("A", 6, 9), ("B", 9, 12), ("C", 8, 10), ("D", 12, 15),    ("E", 15, 18), ("F", 14, 16), ("G", 5, 18), ("H", 10, 11)}; var chosen = new List<string>();int freeFrom = int.MinValue;foreach (var b in bookings.OrderBy(b => b.End))      // earliest finish first{    if (b.Start < freeFrom) continue;                 // clashes with the last one taken    chosen.Add(b.Id);    freeFrom = b.End;}Console.WriteLine($"{chosen.Count}: {string.Join(", ", chosen)}");
Output
4: A, H, D, E

Sorting is O(n log n); the scan is O(n). Trying every subset of bookings would be O(2ⁿ).

Making change

To pay an amount with the fewest coins, the greedy rule is “always use the biggest coin that fits”. For normal currencies (1, 2, 5, 10, 20, …) this is optimal. For other coin systems it is not:

GreedyChange.cs
Console.WriteLine(string.Join(" + ", GreedyChange(48, new[] { 20, 10, 5, 2, 1 })));Console.WriteLine(string.Join(" + ", GreedyChange(6, new[] { 4, 3, 1 })));   // 3 + 3 is better static List<int> GreedyChange(int amount, int[] coinsLargestFirst){    var used = new List<int>();    foreach (int c in coinsLargestFirst)        while (amount >= c) { used.Add(c); amount -= c; }    return used;}
Output
20 + 20 + 5 + 2 + 1
4 + 1 + 1

With coins 4, 3 and 1, greedy pays 6 as 4 + 1 + 1 (three coins), but 3 + 3 needs only two. Taking the 4 felt best but blocked the better answer. Problems like this need dynamic programming (later in this module), which considers every option without trying every combination.

Fractional knapsack

A truck can carry 50 kg. Materials have a value and a weight, and you may take part of any material. Greedy by value per kilogram is optimal: fill up with the most valuable material per kg first. (If items cannot be split, the “0/1 knapsack”, greedy fails and you need DP again.)

FractionalKnapsack.cs
var items = new[]{    (Name: "Steel", Value: 60, Weight: 10),    (Name: "Copper", Value: 100, Weight: 20),    (Name: "Timber", Value: 120, Weight: 30)}; double capacity = 50, total = 0;foreach (var it in items.OrderByDescending(i => (double)i.Value / i.Weight))   // best value per kg first{    double take = Math.Min(it.Weight, capacity);    total += take * it.Value / it.Weight;    capacity -= take;    Console.WriteLine($"{it.Name}: {take} of {it.Weight} kg");    if (capacity == 0) break;}Console.WriteLine($"Value: {total}");
Output
Steel: 10 of 10 kg
Copper: 20 of 20 kg
Timber: 20 of 30 kg
Value: 240

How to approach a greedy idea

  1. State the greedy rule precisely (“sort by end time, take whatever fits”).
  2. Hunt for a counterexample with small inputs, as the widget does for earliest start and shortest first. Most wrong greedy rules fail on 3 or 4 items.
  3. If you cannot break it, argue it: an exchange argument (“swapping an optimal choice for the greedy one never makes things worse”) is the usual proof.
  4. If it does break, think about dynamic programming.
ProblemGreedy ruleOptimal?
Most non-overlapping bookingsEarliest finishYes
Fewest coins, standard currencyBiggest coin firstYes
Fewest coins, any coin setBiggest coin firstNo: use DP
Fractional knapsackBest value per weightYes
0/1 knapsackBest value per weightNo: use DP
Minimum spanning treeCheapest safe edgeYes
Compression codes (Huffman)Merge the two rarest symbolsYes
Quick check

Why does “earliest start” fail for interval scheduling?

Quick check

Coins are 1, 5 and 6. What does greedy pay for 10, and what is optimal?

Practice

Each truck trip can carry at most two loads, with a total weight up to limit. Find the fewest trips for all loads. { 3, 2, 2, 1 } with limit 3 → 3 trips; { 3, 5, 3, 4 } with limit 5 → 4 trips. Use a greedy rule.

Show a hint

Sort the loads. The heaviest remaining load must go on some trip; pair it with the lightest remaining load if they fit together, otherwise it goes alone. Two pointers make this O(n log n).

Show the solution
Solution.cs
Console.WriteLine(MinTrips(new[] { 3, 2, 2, 1 }, 3));Console.WriteLine(MinTrips(new[] { 3, 5, 3, 4 }, 5)); static int MinTrips(int[] loads, int limit){    var w = loads.OrderBy(x => x).ToArray();    int light = 0, heavy = w.Length - 1, trips = 0;    while (light <= heavy)    {        if (w[light] + w[heavy] <= limit) light++;   // the lightest rides along        heavy--;                                      // the heaviest always goes now        trips++;    }    return trips;}
Output
3
4

Remember

  • Greedy algorithms take the locally best choice and never undo it: simple and fast.
  • They are only correct for some problems; test small counterexamples first.
  • Prove a greedy rule with an exchange argument: the greedy choice is never worse than an optimal one.
  • Earliest finish, standard coins, fractional knapsack, MST and Dijkstra are greedy successes; general coin sets and 0/1 knapsack need DP.
0 of 50 lessons completed