Minimum Spanning Tree Pattern
A Minimum Spanning Tree (MST) is a subset of edges in a connected, undirected, weighted graph that connects all vertices with the minimum possible total edge weight and contains no cycles. Two canonical algorithms solve it: Kruskal's, which sorts edges and uses Union-Find to detect cycles, and Prim's, which grows the tree one vertex at a time using a min-heap. MST is the standard answer for 'connect all the things at minimum cost' problems.
By Alex Chen, Founder, InterviewChamp.AI · Last verified
Complexity
- Time
- O(E log E) for Kruskal (sort dominates); O((V + E) log V) for Prim with a binary heap
- Space
- O(V + E)
When to use Minimum Spanning Tree
- The problem asks for the minimum cost to connect every node in a graph — laying cables, building roads, wiring components.
- The graph is undirected, weighted, and you need a connected subset of edges without cycles.
- You need exactly n - 1 edges to span n nodes and minimize the total weight.
- The problem can be modeled as 'pick a subset of pairwise connections to make the graph one connected piece'.
- Variants ask for second-best MST, MST with constraints on certain edges, or MST under updates to the edge list.
Template
class UnionFind {
constructor(n) {
this.parent = Array.from({ length: n }, (_, i) => i);
this.rank = new Array(n).fill(0);
}
find(x) {
if (this.parent[x] !== x) this.parent[x] = this.find(this.parent[x]);
return this.parent[x];
}
union(x, y) {
const px = this.find(x);
const py = this.find(y);
if (px === py) return false;
if (this.rank[px] < this.rank[py]) this.parent[px] = py;
else if (this.rank[px] > this.rank[py]) this.parent[py] = px;
else { this.parent[py] = px; this.rank[px]++; }
return true;
}
}
function kruskalMST(numNodes, edges) {
edges.sort((a, b) => a[2] - b[2]);
const uf = new UnionFind(numNodes);
let totalWeight = 0;
const mstEdges = [];
for (const [u, v, w] of edges) {
if (uf.union(u, v)) {
totalWeight += w;
mstEdges.push([u, v, w]);
if (mstEdges.length === numNodes - 1) break;
}
}
return mstEdges.length === numNodes - 1 ? totalWeight : -1;
}
function primMST(numNodes, edges) {
const graph = Array.from({ length: numNodes }, () => []);
for (const [u, v, w] of edges) {
graph[u].push([v, w]);
graph[v].push([u, w]);
}
const visited = new Array(numNodes).fill(false);
const heap = [[0, 0]];
let total = 0;
let count = 0;
while (heap.length > 0 && count < numNodes) {
heap.sort((a, b) => a[0] - b[0]);
const [w, node] = heap.shift();
if (visited[node]) continue;
visited[node] = true;
total += w;
count++;
for (const [next, weight] of graph[node]) {
if (!visited[next]) heap.push([weight, next]);
}
}
return count === numNodes ? total : -1;
}Example LeetCode problems
| # | Problem | Difficulty | Frequency |
|---|---|---|---|
| 1584 | Min Cost to Connect All Points | medium | foundational |
| 1135 | Connecting Cities With Minimum Cost | medium | company favorite |
| 1168 | Optimize Water Distribution in a Village | hard | frequently asked |
| 1489 | Find Critical and Pseudo-Critical Edges in MST | hard | less common |
| 1697 | Checking Existence of Edge Length Limited Paths | hard | frequently asked |
| 1135 | Min Cost to Repair Edges | medium | frequently asked |
| 1631 | Path With Minimum Effort | medium | classic |
| 778 | Swim in Rising Water | hard | classic |
Common mistakes
- Treating Kruskal's union step as cycle-free without actually calling find on both endpoints — adding an edge whose endpoints are already in the same component creates a cycle and breaks the spanning-tree invariant.
- Forgetting that Prim's heap can contain stale entries (a node added at one weight then later at a smaller weight) and processing the larger one first instead of skipping visited nodes on pop.
- Running MST on a disconnected graph and silently returning a forest of n - k edges — always verify that exactly n - 1 edges were selected before returning a result.
- Sorting edges by ascending weight in Kruskal but using a stable in-place sort that mutates the caller's array, causing subtle bugs when the caller reuses the edge list.
- Using MST when the problem actually wants a Steiner tree (subset of terminals must be connected) — these are different problems and MST gives an incorrect lower bound at best.
Frequently asked questions
When should I use Kruskal versus Prim?
Kruskal shines on sparse graphs where E is close to V, because sorting E edges is cheap and Union-Find is near-constant per operation. Prim is better on dense graphs (E close to V^2) when you also have a fast adjacency-list representation; with a heap it stays O((V + E) log V) and avoids sorting the entire edge list.
Is the MST unique?
If all edge weights are distinct, the MST is unique. If some edges share weights, multiple MSTs may exist with the same total cost — picking among them often depends on tie-breaking rules the problem imposes (lexicographic, edge id, etc.).
Can MST handle negative edge weights?
Yes. MST algorithms only compare weights, so negative values are fine. The result still has minimum total weight; it just may be negative. This is one important difference versus shortest-path algorithms like Dijkstra that require non-negative weights.
How do I detect critical and pseudo-critical edges?
A critical edge is one whose removal increases the MST weight (or disconnects the graph). A pseudo-critical edge appears in some MST but not all. The standard approach: compute the baseline MST weight, then for each edge, (1) force-exclude it and recompute — if the new weight is higher, it is critical; (2) force-include it and recompute — if the new weight equals the baseline, it is pseudo-critical.
Solve it now
Free. No sign-up. Python and JavaScript run instantly in your browser.
Practice these live with InterviewChamp.AI
Drill the Minimum Spanning Tree pattern under real interview conditions with instant feedback on your reasoning, complexity claims, and code.
Practice these live with InterviewChamp.AI →