Lesson 22 / 42
Union-Find / Disjoint Set
A structure that tracks disjoint groups and answers 'same group?' and 'merge groups' in near O(1).
Groups as trees
Union-Find (Disjoint Set Union) represents each group as a tree, where every node points to a parent, and the root is its own parent. find(x) walks up to the root to identify x's group; union(x, y) links one root under the other.
Path compression + union by rank
Two optimizations make this near-constant time: path compression flattens the tree during find, and union by rank attaches the smaller tree under the bigger one.
class UnionFind:
def __init__(self, n):
self.parent = list(range(n))
self.rank = [0] * n
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, x, y):
rx, ry = self.find(x), self.find(y)
if rx == ry:
return False
if self.rank[rx] < self.rank[ry]:
rx, ry = ry, rx
self.parent[ry] = rx
if self.rank[rx] == self.rank[ry]:
self.rank[rx] += 1
return True
Output:
uf = UnionFind(5) uf.union(0, 1) uf.find(0) == uf.find(1) # True
Where it's used
Detecting cycles in an undirected graph, Kruskal's MST, counting connected components, and 'friend circles' style problems all lean on Union-Find.
Quick check
Test your understanding of amortized complexity.
Quick check: With path compression and union by rank, find/union run in roughly:
- O(n)
- O(log n)
- O(n log n)
- Nearly O(1) (inverse-Ackermann)
Answer
Nearly O(1) (inverse-Ackermann) — The amortized complexity is O(α(n)), the inverse Ackermann function, which is effectively constant for any realistic n.