๐งฉIntro to Algorithms
Greedy Algorithm Applications
Study smarter with Fiveable
Get study guides, practice questions, and cheatsheets for all your subjects. Join 500,000+ students with a 96% pass rate.
Why This Matters
Greedy algorithms solve optimization problems by making locally optimal choices at each step, aiming for a globally optimal solution. You're being tested not just on knowing these algorithms exist, but on understanding when and why the greedy approach works. That means recognizing two key properties:
- Greedy choice property: a locally optimal choice leads to a globally optimal solution.
- Optimal substructure: an optimal solution contains optimal solutions to its subproblems.
These applications span graph algorithms, data compression, scheduling, and resource allocation. Exam questions frequently ask you to prove greedy correctness, analyze time complexity, or identify when greedy fails. Don't just memorize the algorithms. Know what structural property each problem exploits, and be ready to contrast greedy solutions with dynamic programming alternatives.
Graph Optimization Problems
Graph-based greedy algorithms build solutions edge-by-edge or vertex-by-vertex. The reason this works is that committing to locally best edges or paths doesn't prevent you from finding the global optimum, as long as certain conditions hold (like non-negative weights).
Minimum Spanning Tree (Kruskal's and Prim's Algorithms)
An MST connects all vertices in a weighted, undirected graph with the minimum possible total edge weight. Both Kruskal's and Prim's guarantee optimality, but they use different greedy strategies.
- Kruskal's sorts all edges by weight, then adds them one at a time, skipping any edge that would create a cycle. It uses a Union-Find data structure to check for cycles efficiently. Time complexity: .
- Prim's grows the tree outward from a starting vertex, always adding the cheapest edge that connects a vertex already in the tree to one outside it. With a priority queue, it runs in .
Dijkstra's Shortest Path Algorithm
Dijkstra's finds the shortest path from a single source to all other vertices, but only in graphs with non-negative edge weights.
- Initialize the source distance to 0 and all others to infinity.
- Extract the vertex with the smallest tentative distance from a priority queue.
- For each neighbor, check if going through the current vertex offers a shorter path. If so, update the neighbor's distance (this is called relaxation).
- Repeat until all vertices have been processed.
Time complexity is with a binary heap. The algorithm fails on negative weights because once a vertex is finalized, its distance can't be revised, and a negative edge could later provide a shorter path.
Compare: Kruskal's and Dijkstra's both use greedy edge selection with priority structures, but MST algorithms consider global edge weights while Dijkstra's tracks cumulative path distances. If a question asks about greedy graph algorithms, clarify whether you're optimizing total weight (MST) or path length (shortest path).
Scheduling and Selection Problems
These problems share a common structure: given a set of tasks with constraints, select or order them optimally. The greedy insight is that sorting by the right criterion (usually finish time or deadline) enables optimal sequential selection.
Activity Selection Problem
The goal is to select the maximum number of non-overlapping activities. This is the classic greedy scheduling problem.
- Sort activities by finish time (earliest first).
- Select the first activity.
- For each remaining activity, select it if its start time is at or after the finish time of the last selected activity.
Correctness is proven via an exchange argument: you can take any optimal solution and swap its first activity for the one with the earliest finish time without reducing the count. This shows the greedy choice is always safe.
Interval Scheduling
This is equivalent to activity selection. You're selecting the maximum number of non-overlapping intervals from a set defined by start and end times. Greedy by earliest finish time guarantees leaving maximum room for subsequent intervals. The overall complexity is due to sorting; the selection phase itself is linear.
Job Sequencing with Deadlines
Here the goal shifts: maximize total profit by scheduling jobs before their deadlines, where each job takes one time unit.
- Sort jobs by decreasing profit.
- For each job, assign it to the latest available time slot before its deadline.
- If no slot is available, skip the job.
Prioritizing high-profit jobs ensures maximum total profit when slots are limited.
Compare: Activity Selection maximizes count (sort by finish time) while Job Sequencing maximizes profit (sort by profit). Know which sorting criterion matches which objective.
Data Compression
Greedy compression algorithms build optimal encodings by prioritizing frequent elements. The key mechanism is constructing prefix-free codes, where no codeword is a prefix of another. This guarantees unambiguous decoding.
Huffman Coding
Huffman coding assigns variable-length codes based on character frequency. More frequent characters get shorter codes, which minimizes the expected message length.
- Create a leaf node for each character, weighted by its frequency.
- Insert all nodes into a min-priority queue.
- Extract the two nodes with the lowest frequencies.
- Create a new internal node with these two as children, with frequency equal to their sum.
- Insert the new node back into the queue.
- Repeat steps 3-5 until one node remains. That node is the root of the Huffman tree.
Time complexity: where is the number of distinct characters. Huffman coding is proven optimal among all prefix-free encoding schemes for known frequency distributions.
Huffman Decoding
Decoding reconstructs the original data by traversing the Huffman tree. Read each bit: go left for 0, right for 1. When you reach a leaf, output that character and return to the root. The prefix-free property guarantees there's never ambiguity about where one codeword ends and the next begins. Decoding runs in time for encoded bits, since each bit requires constant work.
Compare: Encoding requires building the tree () while decoding only traverses it ( for bits). Exam questions may ask you to construct a Huffman tree from frequencies or trace a decoding sequence.
Resource Allocation Problems
These problems involve distributing limited resources optimally. Greedy works when items can be ranked by a single efficiency metric and selected accordingly.
Fractional Knapsack Problem
You want to maximize value in a weight-limited knapsack, and items can be divided into fractions (unlike the 0/1 version).
- Compute the value-to-weight ratio for each item.
- Sort items by this ratio in decreasing order.
- Add items greedily. If the next item fits entirely, take all of it. If not, take the fraction that fills the remaining capacity.
Greedy is optimal here because you can take fractions. With 0/1 knapsack, you must take an item entirely or not at all, which means a high-ratio item might waste too much capacity. That's why 0/1 knapsack requires dynamic programming.
Coin Change Problem (Canonical Coin Systems)
The goal is to find the minimum number of coins to make a target amount. The greedy approach is simple: always pick the largest denomination that fits, and repeat.
For canonical coin systems like US coins (1, 5, 10, 25), this works perfectly. But for non-canonical systems, greedy can fail. With coins {1, 3, 4} and target 6, greedy picks {4, 1, 1} (3 coins), but the optimal answer is {3, 3} (2 coins). For non-canonical systems, you need dynamic programming.
Compare: Fractional vs. 0/1 Knapsack is a classic exam question on greedy limitations. Fractional allows greedy (sort by ratio), but 0/1 requires DP because you can't take partial items to "fine-tune" the solution.
Mathematical Applications
Some greedy algorithms solve number-theoretic problems by iteratively selecting the largest valid component.
Egyptian Fraction
An Egyptian fraction represents any fraction as a sum of distinct unit fractions (fractions of the form ).
The greedy algorithm works like this: given , subtract the largest unit fraction that doesn't exceed it, which is . Then repeat on the remainder.
This greedy approach always terminates and produces a valid representation, but it's not always optimal in terms of fewest fractions. The greedy decomposition may produce more terms than the minimum possible.
Quick Reference Table
| Concept | Best Examples |
|---|---|
| Graph optimization (edges/paths) | MST (Kruskal's, Prim's), Dijkstra's |
| Scheduling by finish time | Activity Selection, Interval Scheduling |
| Scheduling by profit | Job Sequencing with Deadlines |
| Data compression | Huffman Coding, Huffman Decoding |
| Resource allocation with divisibility | Fractional Knapsack |
| Greedy works conditionally | Coin Change (canonical systems only) |
| Greedy not optimal | Egyptian Fraction, 0/1 Knapsack, Coin Change (non-canonical) |
| Requires non-negative weights | Dijkstra's Algorithm |
Self-Check Questions
-
Both Activity Selection and Interval Scheduling use the same greedy strategy. What sorting criterion do they share, and why does it guarantee optimality?
-
Compare Kruskal's and Prim's algorithms: what data structures does each use, and how do their greedy selection strategies differ?
-
Why does the greedy approach work for Fractional Knapsack but fail for 0/1 Knapsack? What property does the fractional version have that the 0/1 version lacks?
-
Given a coin system {1, 3, 4}, explain why greedy fails to find the minimum coins for amount 6. What algorithm would you use instead?
-
(FRQ-style) Prove that Dijkstra's algorithm fails on graphs with negative edge weights by constructing a small counterexample. Then explain what property of greedy algorithms is violated.