Kruskal's MST
Kruskal's algorithm finds the cheapest set of edges that connects every node in a graph - the minimum spanning tree. The plan sounds too greedy to be legal: sort all edges by price, then take them cheapest-first, skipping any edge that would close a loop. That simple rule is provably optimal, every time. It is the algorithm behind laying cable, planning pipelines, and even clustering data.
Watch the forest become a tree
Press play - or drag the timeline and step through it yourself.Goal: connect all 7 nodes so the total edge weight is as small as possible. The rule: always take the cheapest edge left, unless it would close a loop.
Complexity
Wiring seven villages for internet
Seven villages need internet, and every possible cable route between two villages has a price. Your budget is tight, so you sort the routes by price and build the cheapest ones first. Before building a route, you ask one question: are these two villages already connected through cables we built earlier? If yes, the new cable is wasted money - skip it. If no, build it. Halfway through, the map looks odd: three small networks that do not touch each other. That is fine - the cheap cables you keep buying stitch them together, and with 6 cables all 7 villages are online.
How it works, step by step
Sort every edge by weight, cheapest first. This order is the whole idea.
Give each node its own group. A union-find structure tracks the groups fast.
Take the next cheapest edge and look up the group of each endpoint.
Different groups? Take the edge and merge the two groups into one.
Same group? The edge would close a loop - skip it, and never look at it again.
Stop after V - 1 edges are kept. Until that moment you have a *forest* of separate trees - the last merge turns it into one spanning tree.
The code, in JavaScript
The complete algorithm with a compact union-find (DSU) inside. `find` answers "which group?", `parent[ru] = rv` merges groups. The sort is the expensive part - everything after it is nearly linear.
function find(parent, x) {
while (parent[x] !== x) {
parent[x] = parent[parent[x]]; // path halving: point at grandparent
x = parent[x];
}
return x;
}
function kruskal(nodes, edges) {
const parent = {};
for (const n of nodes) parent[n] = n; // everyone starts alone
// Cheapest first - this order is the whole idea.
const sorted = [...edges].sort((a, b) => a.w - b.w);
const tree = [];
let total = 0;
for (const { u, v, w } of sorted) {
const ru = find(parent, u);
const rv = find(parent, v);
if (ru === rv) continue; // same group: would close a loop
parent[ru] = rv; // merge the two groups
tree.push([u, v, w]);
total += w;
if (tree.length === nodes.length - 1) break; // tree complete
}
return { tree, total };
}
const nodes = ["A", "B", "C", "D", "E", "F", "G"];
const edges = [
{ u: "A", v: "B", w: 2 }, { u: "D", v: "F", w: 3 },
{ u: "C", v: "E", w: 4 }, { u: "A", v: "C", w: 5 },
{ u: "B", v: "C", w: 6 }, { u: "F", v: "G", w: 7 },
{ u: "D", v: "G", w: 8 }, { u: "B", v: "D", w: 9 },
{ u: "D", v: "E", w: 11 }, { u: "E", v: "G", w: 12 },
{ u: "E", v: "F", w: 14 },
];
kruskal(nodes, edges).total;
// → 30 (6 edges kept: A-B, D-F, C-E, A-C, F-G, B-D)Dry run: building the MST edge by edge
The same steps the visualizer plays, written as a table. Generated by running the real algorithm - not written by hand.
| Step | Edge | What happened |
|---|---|---|
| 1 | 1 | Next cheapest edge: A-B, weight 2. Are A and B already connected? |
| 2 | 1 | A and B were both alone - take it. They start set 1. Kept: 1 of 6. |
| 3 | 2 | Next cheapest edge: D-F, weight 3. Are D and F already connected? |
| 4 | 2 | D and F were both alone - take it. They start set 2. Kept: 2 of 6. |
| 5 | 3 | Next cheapest edge: C-E, weight 4. Are C and E already connected? |
| 6 | 3 | C and E were both alone - take it. They start set 3. Kept: 3 of 6. |
| 7 | 4 | Next cheapest edge: A-C, weight 5. Are A and C already connected? |
| 8 | 4 | Set 1 and set 3 were different groups - take it, they merge into set 1. Kept: 4 of 6. |
| 9 | 5 | Next cheapest edge: B-C, weight 6. Are B and C already connected? |
| 10 | 5 | B and C are both in set 1 already - this edge would close a loop. Skip it for good. |
| 11 | 6 | Next cheapest edge: F-G, weight 7. Are F and G already connected? |
| 12 | 6 | G was alone - take it. G joins set 2. Kept: 5 of 6. |
| 13 | 7 | Next cheapest edge: D-G, weight 8. Are D and G already connected? |
| 14 | 7 | D and G are both in set 2 already - this edge would close a loop. Skip it for good. |
| 15 | 8 | Next cheapest edge: B-D, weight 9. Are B and D already connected? |
| 16 | 8 | Set 1 and set 2 were different groups - take it, they merge into set 1. Kept: 6 of 6. |
Good choice when…
- You need the cheapest way to connect everything: network cables, water pipes, roads, circuit board wiring.
- The graph is sparse or arrives as a plain edge list - Kruskal eats that format directly, no adjacency structure needed.
- You want clusters: stop early with k groups left and you have single-linkage clustering for free.
- The edges are already sorted (or sortable cheaply) - then Kruskal is almost linear thanks to union-find.
Bad choice when…
- You need the shortest path between two nodes - an MST minimizes total wiring, not your route; that is Dijkstra's job.
- The graph is dense (close to V² edges) - Prim's with a heap avoids sorting a huge edge list and wins.
- The graph is directed - spanning trees are an undirected idea; the directed version (arborescence) needs a different algorithm entirely.
Common mistakes
- Kruskal builds a forest, not a tree, for most of its run. Separate components floating around mid-way is normal and correct - they only merge into one tree at the very end. Do not "fix" it.
- It needs union-find. Checking "would this close a loop?" with a DFS per edge works but costs O(E·V) - people ship that by accident and wonder why big inputs crawl. DSU makes the check near O(1).
- Equal weights make the MST non-unique: two runs (or two libraries) can return different edge sets, both optimal. Tests that compare exact edges are brittle - compare the total weight instead.
- A disconnected graph has no spanning tree - Kruskal does not throw, it silently returns a forest. Always check that you kept exactly V - 1 edges; fewer means the graph was not connected.
Kruskal's MST vs. its closest relatives
| Algorithm | Best | Average | Worst | Space | Stable |
|---|---|---|---|---|---|
| Kruskal's MSTthis page | O(E log E) | O(E log E) | O(E log E) | O(V) | - |
| Union–Find (DSU) | O(1) | O(α(n)) | O(α(n)) | O(n) | - |
| Dijkstra's Algorithm | O(E log V) | O(E log V) | O(E log V) | O(V) | - |
| Breadth-First Search | O(V + E) | O(V + E) | O(V + E) | O(V) | - |