Idea: Grow a tree from a starting node, always adding the cheapest edge that connects the tree to a new vertex.
Algorithm:
Step 0 / 9 — click ▶ to start
PrimMST(G, w, r): // r = arbitrary root
for each v in V:
key[v] = ∞ // cheapest edge-weight from T to v
parent[v] = NIL
insert v into PQ with key[v]
key[r] = 0
while PQ is not empty:
u = extract-min(PQ) // vertex with smallest key = cheapest to connect to T
for each v in adj[u]: // T just grew by u; update u's neighbours
if v in PQ and w(u,v) < key[v]:
parent[v] = u
key[v] = w(u,v)
decrease-key(PQ, v, key[v])
extract-min: O(\log V) each (binary heap)decrease-key: O(\log V) each| Implementation | extract-min |
decrease-key |
Total |
|---|---|---|---|
| Binary heap | O(\log V) | O(\log V) | O((V+E)\log V) = O(E \log V) |
| Fibonacci heap | O(\log V) amortized | O(1) amortized | O(E + V \log V) |
Prim’s algorithm correctly computes an MST.
Invariant: After each iteration, the tree T is a subgraph of some minimum spanning tree M.
Base case: T = \{r\} (single node, no edges) — trivially a subgraph of any MST.
Inductive step: Suppose T \subseteq M for some MST M, and Prim’s adds edge e = (u, v) where u \in T, v \notin T.
We need to show T \cup \{e\} is a subgraph of some MST.
Case 1: e \in M. Then T \cup \{e\} \subseteq M. Done.
Case 2: e \notin M. Consider M \cup \{e\} — it contains a cycle C.
Construct M' = M \cup \{e\} - \{f\}:
Result: Boruvka’s algorithm correctly computes an MST.
T(V,E) = O(E \log V)
A general technique for proving greedy algorithms are optimal:
| Algorithm | What is exchanged | Why it works |
|---|---|---|
| Kruskal’s | Cheaper edge e replaces heavier e' in cycle | Cut/cycle property |
| Prim’s | Cheapest crossing edge e replaces heavier f | Cut property |
| Interval Scheduling | Earlier-finishing job replaces later one | Exchange preserves feasibility |
| Huffman | Low-frequency characters get deeper codes | Optimal prefix code property |
| Kruskal’s | Prim’s | Boruvka’s | |
|---|---|---|---|
| Strategy | Global — sort all edges | Local — grow from a root | Distributed — each component acts independently |
| Data Structure | Union-Find (DSU) | Priority Queue (min-heap) | Component edge lists |
| Core Operation | Cycle detection via find |
Extract cheapest crossing vertex | Find cheapest outgoing edge per component |
| Time Complexity | O(E \log V) | O(E \log V) | O(E \log V) |
| Best For | Sparse graphs | Dense graphs (with Fibonacci heap) | Parallel / distributed systems |
| Parallelism | Difficult (sequential edge processing) | Difficult (sequential growth) | Natural (independent components) |
Sparse graphs (E \approx V): All three run in O(V \log V). Kruskal’s is simplest to implement.
Dense graphs (E \approx V^2): Prim’s with Fibonacci heap gives O(V^2), which beats O(V^2 \log V).
Parallel/distributed environment: Boruvka’s is the natural choice — each round is embarrassingly parallel.
Educational value:
| Algorithm | DS | Complexity | Key Idea |
|---|---|---|---|
| Kruskal’s | Union-Find | O(E \log V) | Sort edges, Union-Find for cycles |
| Prim’s | Priority Queue | O(E \log V) | Grow from root, extract-min |
| Boruvka’s | Component lists | O(E \log V) | Parallel component merging |
CS F364: Design & Analysis of AlgorithmsTulasimohan Molli