Minimum Spanning Tree
Find the subset of edges that connects all vertices with minimum total weight. Kruskal's and Prim's are the two classic approaches.
Interactive visualization
Use the controls to connect the idea to concrete operations before diving into the full write-up.
MST weight: 0
Family
Graphs
Nodes, edges, traversal, connectivity, and shortest paths.
Builds on
4 topics
Read these first if you want the surrounding context.
Unlocks
0 next topics
Use these follow-ups when you want to apply the current idea in a richer setting.
Learning paths
1
This topic appears in curated progressions so you can keep moving without guessing the next step.
Problem
Given a connected, undirected, weighted graph , find a spanning tree such that the total edge weight is minimized. A spanning tree connects all vertices using exactly edges with no cycles.
Intuition
A spanning tree is any acyclic subgraph that connects every vertex. Among all possible spanning trees, the MST has the smallest total weight. Two properties drive the classic algorithms:
- Cut property: for any cut , the minimum-weight edge crossing the cut is in every MST (assuming distinct weights). This justifies greedily picking light edges.
- Cycle property: for any cycle in , the maximum-weight edge in that cycle is not in any MST. This justifies discarding heavy edges that would form cycles.
Kruskal’s algorithm
Sort all edges by weight, then greedily add each edge if it doesn’t create a cycle. Use Union-Find to check connectivity in near-constant time.
Kruskal(V, E):
sort E by weight ascending
UF ← UnionFind(V)
mst ← []
for (u, v, w) in E:
if UF.find(u) ≠ UF.find(v):
mst.append((u, v, w))
UF.union(u, v)
if |mst| == |V| - 1:
break
return mst
The bottleneck is the sort: . Each union/find is , so the Union-Find work totals .
Prim’s algorithm
Grow the MST from a starting vertex by always adding the cheapest edge connecting a tree vertex to a non-tree vertex. This is Dijkstra’s algorithm but tracking edge weight instead of cumulative distance.
Prim(graph, start):
inMST ← {start}
pq ← MinHeap()
for (v, w) in graph[start]:
pq.insert((w, start, v))
mst ← []
while |inMST| < |V|:
(w, u, v) ← pq.extract_min()
if v in inMST: continue
inMST.add(v)
mst.append((u, v, w))
for (next, nw) in graph[v]:
if next ∉ inMST:
pq.insert((nw, v, next))
return mst
Kruskal vs Prim
| Kruskal | Prim (binary heap) | |
|---|---|---|
| Best for | Sparse graphs () | Dense graphs () |
| Data structure | Union-Find | Priority queue |
| Edge list needed? | Yes (sorts all edges) | No (adjacency list) |
| Parallelizable | Easy (filter-kruskal) | Harder |
For sparse graphs, Kruskal’s dominates because is small. For dense graphs, Prim’s with an adjacency matrix runs in , avoiding the costly sort when .
Properties of MSTs
- Uniqueness: if all edge weights are distinct, the MST is unique. With ties, multiple MSTs may exist but all share the same total weight.
- Cycle property: the heaviest edge in any cycle is never in an MST.
- Cut property: the lightest edge across any cut is always in the MST (with distinct weights).
- Edge count: always exactly .
- Minimax path: the MST minimizes the maximum edge weight on the path between any two vertices.
Common variations
- Maximum spanning tree: negate all weights and run Kruskal/Prim, or sort descending in Kruskal’s.
- Second-best MST: for each non-MST edge , find the max-weight edge on the MST path . Swap the pair with smallest difference. with LCA.
- MST on complete graphs: Prim’s with a flat array runs in , matching the number of edges.
- Steiner tree: MST over a subset of vertices — NP-hard in general.
Complexity
| Algorithm | Time | Space |
|---|---|---|
| Kruskal (Union-Find) | ||
| Prim (binary heap) | ||
| Prim (Fibonacci heap) | ||
| Prim (array, no heap) | ||
| Borůvka |
Key takeaways
- Cut property is the core insight — the lightest edge crossing any cut must be in the MST, which is why both Kruskal’s and Prim’s work by greedily picking minimum-weight edges.
- Kruskal’s for sparse, Prim’s for dense — Kruskal’s wins when ; Prim’s array version wins when because it avoids sorting all edges.
- Union-Find makes Kruskal’s practical — without path compression and union by rank, cycle detection would dominate the runtime.
- Distinct weights guarantee a unique MST — with ties multiple MSTs may exist, but they all share the same total weight.
- MST ≠ shortest paths — the MST minimizes total edge weight (and minimax path weight), while Dijkstra minimizes cumulative distance from a source.
Practice problems
| Problem | Difficulty | Key idea |
|---|---|---|
| Min Cost to Connect All Points | 🟡 Medium | Build MST on a complete graph of points using Kruskal’s or Prim’s |
| Connecting Cities With Minimum Cost | 🟡 Medium | Classic MST — sort edges and use Union-Find to connect all cities |
| Optimize Water Distribution in a Village | 🔴 Hard | Add a virtual node for wells, reducing the problem to standard MST |
| Find Critical and Pseudo-Critical Edges in MST | 🔴 Hard | Force-include and force-exclude each edge to classify criticality |
| Network Delay Time | 🟡 Medium | Dijkstra on weighted graph — contrasts with MST by minimizing path distance instead of total weight |
Relation to other topics
- Union-Find: Kruskal’s is the canonical use case — Union-Find provides near- cycle detection.
- Dijkstra: Prim’s shares the same structure (greedy expansion with a priority queue), but uses edge weight rather than cumulative distance from the source.
- Borůvka: each component picks its cheapest outgoing edge simultaneously, merging in phases. Useful in parallel and distributed settings.
Build on these first
These topics supply the mental model or underlying structure that this page assumes.
Graph Fundamentals
Learn the core language of nodes, edges, paths, and cycles. Graphs are the broad container that trees, BFS, DFS, Dijkstra, and MST all live inside.
Union-Find (Disjoint Set)
Track connected components efficiently with near-constant time union and find operations. The backbone of Kruskal's MST and dynamic connectivity.
Heap & Priority Queue
A complete binary tree where every parent is smaller (or larger) than its children. The backbone of Dijkstra, event scheduling, and top-k problems.
Greedy Algorithms
Make the locally optimal choice at each step to find a global optimum. Greedy works when the problem has optimal substructure and the greedy choice property.
Related directions
These topics live nearby conceptually, even if they are not direct prerequisites.
More from Graphs
Stay in the same family when you want parallel variations of the same mental model.
BFS — Breadth-First Search
Explore a graph level by level using a queue. BFS finds the shortest path in unweighted graphs and is fundamental to many graph algorithms.
DFS — Depth-First Search
Dive as deep as possible before backtracking. DFS is essential for detecting cycles, topological sorting, and exploring all paths in a graph.
Graph Fundamentals
Learn the core language of nodes, edges, paths, and cycles. Graphs are the broad container that trees, BFS, DFS, Dijkstra, and MST all live inside.
0-1 BFS
Find shortest paths in graphs whose edge weights are only 0 or 1 using a deque instead of a heap.
Paths that include this topic
Follow one of these sequences if you want a guided next step instead of open-ended browsing.
Graph toolkit
Start with graph shape, then layer traversal, connectivity structure, and both single-source and all-pairs path algorithms.
From the blog
Pair the atlas with recent writing from the rest of the site when you want a broader engineering perspective alongside the topic graph.