CO2 Topic 17 of 34
Kruskal's minimum spanning tree
Sort all edges by weight and take each one unless it would close a cycle. A union–find structure makes the cycle test almost free.
You will be able to
- Trace Kruskal's algorithm, listing accepted and rejected edges
- Implement union–find with union by rank and path compression
- State the running time O(E log E) and where it comes from
- Choose between Prim and Kruskal for a given graph
Read
- Textbook 1, §23.2 Kruskal's algorithm and §21.1–21.3 Disjoint sets
- Textbook 2, §4.5 Minimum-cost spanning trees
- Textbook 3, §8 The disjoint set class and §9.5.2 Kruskal's algorithm
Watch
Intuition
Prim grows one tree. Kruskal looks at the whole graph at once: sort every edge from cheapest to most expensive, and accept an edge unless it connects two vertices that are already connected (which would close a cycle). Early on you get many small pieces (a forest); accepted edges merge pieces until one tree remains.
The question "are u and v already connected?" is answered by a union–find (disjoint-set) structure: each piece is a set; find(x) names x's set, union(a, b) merges two sets.
How it works
Same graph as topic 16 (A–B 4, A–C 3, B–C 1, B–D 2, C–D 4, C–E 5, D–E 7, D–F 6, E–F 2, E–G 3, F–G 4). Edges in sorted order:
| Edge | Weight | Same set already? | Action | Sets afterwards |
|---|---|---|---|---|
| B–C | 1 | no | accept | {B,C} |
| B–D | 2 | no | accept | {B,C,D} |
| E–F | 2 | no | accept | {B,C,D}, |
| A–C | 3 | no | accept | {A,B,C,D}, |
| E–G | 3 | no | accept | {A,B,C,D}, |
| A–B | 4 | yes | reject (cycle) | |
| C–D | 4 | yes | reject (cycle) | |
| F–G | 4 | yes | reject (cycle) | |
| C–E | 5 | no | accept | {A,…,G} — done |
Six edges accepted, total 1 + 2 + 2 + 3 + 3 + 5 = 16 — the same MST that Prim found. The remaining edges (D–F 6, D–E 7) are never needed; you can stop once V − 1 edges are accepted.
Union–find tricks:
- union by rank: attach the shorter tree under the taller one, so trees stay shallow;
- path compression: during
find, point every visited node straight at the root.
Together they make each operation run in α(n) amortised time — the inverse Ackermann function, which is at most 4 for any input that fits in the universe.
Python implementation
class DisjointSet:
def __init__(self, items):
self.parent = {x: x for x in items}
self.rank = {x: 0 for x in items}
def find(self, x):
if self.parent[x] != x:
self.parent[x] = self.find(self.parent[x]) # path compression
return self.parent[x]
def union(self, a, b):
ra, rb = self.find(a), self.find(b)
if ra == rb:
return False # already in the same set
if self.rank[ra] < self.rank[rb]:
ra, rb = rb, ra
self.parent[rb] = ra # union by rank
if self.rank[ra] == self.rank[rb]:
self.rank[ra] += 1
return True
def kruskal(vertices, edges):
ds, mst, total = DisjointSet(vertices), [], 0
for u, v, w in sorted(edges, key=lambda e: e[2]):
if ds.union(u, v): # accept unless it closes a cycle
mst.append((u, v, w))
total += w
if len(mst) == len(vertices) - 1:
break
return mst, total
if __name__ == "__main__":
edges = [("A", "B", 4), ("A", "C", 3), ("B", "C", 1), ("B", "D", 2), ("C", "D", 4), ("C", "E", 5),
("D", "E", 7), ("D", "F", 6), ("E", "F", 2), ("E", "G", 3), ("F", "G", 4)]
mst, total = kruskal("ABCDEFG", edges)
print(mst) # [('B', 'C', 1), ('B', 'D', 2), ('E', 'F', 2), ('A', 'C', 3), ('E', 'G', 3), ('C', 'E', 5)]
print(total) # 16
Complexity
| Step | Time |
|---|---|
| sort the edges | O(E log E) = O(E log V) |
| E union–find operations | O(E · α(V)) — practically linear |
| total | O(E log E) |
Prim or Kruskal? Both give an MST. Kruskal is simple and great when the edges come as a list or the graph is sparse; Prim with a heap (or the O(V²) array version) suits dense graphs and adjacency structures.
Common mistakes
- Rejecting an edge just because one endpoint is already "used". Reject only when both endpoints are in the same set.
- Union without find. Always union the roots returned by find, not the vertices themselves.
- Forgetting ties. Equal weights can be taken in any order; different orders may give different (but equally cheap) MSTs.
- Running Kruskal on a directed graph. MSTs are for undirected graphs; the directed version (arborescence) needs a different algorithm.
Problems
- Run Kruskal on the graph with edges P–Q 6, P–R 1, Q–R 5, Q–S 3, R–S 5, R–T 6, S–T 4, S–U 2, T–U 6, list accepted and rejected edges, and give the MST weight.
- Lab: implement
DisjointSetand use it to count the connected components after reading a list of friendships. - Lab: clustering — run Kruskal but stop when k sets remain; the result groups points into k clusters with maximum spacing.
- Show that the heaviest edge on any cycle of a graph (if strictly heaviest) is in no MST.
- Compare the number of union–find operations Kruskal performs with the number of heap operations Prim performs on the example graph.
Quiz
GATE-style practice
Questions in the GATE CSE formats: MCQ (one correct option), MSQ (one or more correct options) and NAT (type a number). Each comes with a full worked solution. These are original practice questions, not past papers; for the real thing, see the official GATE site and its previous question papers.