A Bit of History
The minimum spanning tree problem has a surprisingly deep and international history. The first known algorithm was published in 1926 by Czech mathematician Otakar Borůvka, motivated by the very practical problem of designing an efficient electrical network for Moravia. His method (grow many tiny trees in parallel, then keep merging them) predates modern computers entirely. Three decades later, American mathematician Joseph Kruskal published the elegant "sort edges globally, add greedily" version presented here — in the very same 1956 issue of the Proceedings of the American Mathematical Society where he also happened to give one of the first published proofs that a related conjecture (about partial orders) held. That same year, Robert Prim independently rediscovered and refined a different MST strategy (grow a single tree outward, covered in the Prim's Algorithm deep dive) — a reminder that important algorithms are often "in the air" and discovered by multiple people from different angles at nearly the same time.
Working Principle
A minimum spanning tree (MST) of a connected, weighted, undirected graph is a subset of edges that (a) connects all vertices, (b) forms no cycle (hence "tree" — exactly \(n-1\) edges, per the induction proof in Part 1), and (c) has the smallest possible total edge weight among all such spanning trees.
Kruskal's algorithm builds one greedily:
- Sort all edges by weight, ascending.
- Initialize an empty edge set (the growing forest).
- For each edge \((u,v)\) in sorted order: if \(u\) and \(v\) are not already connected by edges already chosen, add \((u,v)\) to the MST. Otherwise, skip it (adding it would create a cycle).
- Stop once \(n-1\) edges have been added (the tree spans every vertex).
Analogy: Building Roads on a Budget, Cheapest First
Imagine a town planner connecting \(n\) villages with the cheapest possible road network. She sorts every candidate road by construction cost and builds the cheapest one first. She keeps building roads in increasing-cost order, but skips any road that would just create a redundant loop between villages already connected by existing roads — that road adds cost without adding connectivity. She stops the moment every village is reachable from every other. This is Kruskal's algorithm exactly, and it is provably the cheapest possible network — no cleverer plan can do better.
Union-Find: Detecting Cycles Fast
The one non-trivial engineering detail is step 3: "are \(u\) and \(v\) already connected?" Doing this with a fresh graph traversal every time would be far too slow. Instead, Kruskal's algorithm pairs naturally with the Union-Find (Disjoint Set Union) data structure, which tracks a partition of vertices into connected groups and supports two near-constant-time operations: find(v) (which group is \(v\) in?) and union(u, v) (merge \(u\)'s and \(v\)'s groups). Two path-compression and union-by-rank optimizations push each operation's amortized cost down to \(O(\alpha(n))\) — the inverse Ackermann function, which is less than 5 for any input size that could ever physically be stored, making it "essentially constant" in practice.
Worked Example
Four vertices \(A, B, C, D\) with edges (sorted by weight): \(A\text{-}C\) (1), \(B\text{-}C\) (1), \(B\text{-}D\) (1), \(A\text{-}B\) (4), \(C\text{-}D\) (5).
flowchart LR
A ---|"1 (add)"| C
B ---|"1 (add)"| C
B ---|"1 (add)"| D
A -.->|"4 (skip: cycle)"| B
C -.->|"5 (skip: cycle)"| D
Processing in weight order: \(A\text{-}C\) (add, connects two new components), \(B\text{-}C\) (add, connects \(B\) to the growing \(\{A,C\}\) group), \(B\text{-}D\) (add, connects \(D\) — now all 4 vertices are in one component with 3 edges, exactly \(n-1\), so we stop). \(A\text{-}B\) and \(C\text{-}D\) are never even examined — the algorithm terminates as soon as the tree is complete.
Why Greedy Works: The Cut & Cycle Properties
Two lemmas, both provable by the exchange-argument style of proof by contradiction from Part 1, justify Kruskal's greediness:
- Cut property: for any partition of the vertices into two non-empty groups, the minimum-weight edge crossing that partition belongs to some MST. (If it didn't, swapping it into any candidate MST in place of a more expensive crossing edge would strictly reduce total weight — contradicting that candidate's minimality.)
- Cycle property: for any cycle in the graph, the maximum-weight edge on that cycle belongs to no MST (removing it can only ever reduce cost while an alternate path around the cycle preserves connectivity).
Kruskal's algorithm is exactly repeated application of the cut property: at every step, the cheapest untried edge is the minimum-weight edge crossing the cut between "the two components it would connect" — so adding it is always safe.
Complexity Analysis
Sorting the \(E\) edges dominates: \(O(E \log E)\). Since \(E \leq V^2\), this is also \(O(E \log V)\). The Union-Find operations that follow contribute only \(O(E\, \alpha(V))\), which is dwarfed by the sort.
$$\text{Time: } O(E \log E) \qquad \text{Space: } O(V + E)$$
Implementation
Real-World Applications
Network Design and Single-Linkage Clustering
Utility companies use MST algorithms to design the cheapest possible electrical grid, pipeline network, or fiber-optic backbone that still connects every required location — precisely Borůvka's original 1926 motivation. In data science, single-linkage hierarchical clustering is essentially a Kruskal-style process: repeatedly merge the two closest clusters (the cheapest "edge" between cluster representatives) until all points form one structure, then cut the most expensive remaining edges to reveal natural clusters.
Exercises
- Run Kruskal's algorithm by hand on a 5-vertex graph of your own design, listing the order edges are considered and which are skipped.
- Prove that if all edge weights in a graph are distinct, the MST is unique (hint: use the cycle property — for any two candidate MSTs, find a cycle-forming edge swap that would strictly improve one of them unless they're identical).
- Explain why Kruskal's algorithm still works correctly (just examines more edges before finding the last useful one) if the input graph is disconnected — what does it compute instead of a single spanning tree?
- Challenge: Implement Union-Find without path compression or union-by-rank, and construct a sequence of unions that forces \(O(n)\) time for a single
findcall — then explain which optimization (path compression, union by rank, or both) is responsible for preventing this degenerate case.
Limitations
Sorting Dominates on Dense Graphs
Kruskal's \(O(E \log E)\) sort becomes a real bottleneck on very dense graphs (where \(E\) is close to \(V^2\)) — Prim's algorithm, which grows a single tree using a priority queue over vertices rather than sorting all edges upfront, is often preferred there. Also note: Kruskal's requires global knowledge of all edges before starting (the sort step), which makes it a poor fit for streaming or online graph-construction scenarios where edges arrive one at a time — Prim's incremental growth adapts to that setting more naturally.