Union-Find is a data structure for maintaining disjoint sets. It supports finding an element’s representative and unioning two groups.
It is especially useful for connectivity problems where edges are added over time.
Core Idea
Each element points to a parent. A representative, or root, identifies the whole group. find(x) returns the representative for x. union(a, b) merges the groups containing a and b.
If two elements have the same representative, they are in the same group.
Class Contract
UnionFind(size) creates size separate groups numbered from 0 through size - 1.
find(x) returns the representative of x’s current group. union(a, b) merges the two groups and returns whether a merge actually happened in the optimized version.
Python Example
class UnionFind:
def __init__(self, size):
self.parent = list(range(size))
def find(self, x):
while self.parent[x] != x:
x = self.parent[x]
return x
def union(self, a, b):
root_a = self.find(a)
root_b = self.find(b)
if root_a != root_b:
self.parent[root_b] = root_aThis basic version shows the interface before optimizations.
How Find Represents Groups
The root of a parent tree is the representative. If find(2) and find(5) return the same root, then 2 and 5 are in the same group.
The representative does not usually matter as a meaningful value. It is just a stable identifier for the group.
Optimized Version
Practical Union-Find usually includes path compression and union by size:
class UnionFind:
def __init__(self, size):
self.parent = list(range(size))
self.size = [1] * size
def find(self, x):
if self.parent[x] != x:
self.parent[x] = self.find(self.parent[x])
return self.parent[x]
def union(self, a, b):
root_a = self.find(a)
root_b = self.find(b)
if root_a == root_b:
return False
if self.size[root_a] < self.size[root_b]:
root_a, root_b = root_b, root_a
self.parent[root_b] = root_a
self.size[root_a] += self.size[root_b]
return TrueReturning False when the roots match is useful for cycle detection.
Complexity
With path compression and union by size or rank, operations are effectively constant for practical algorithm constraints. More precisely, they are amortized inverse-Ackermann time, which grows extremely slowly.
Common Confusions
Union-Find answers connectivity membership. It does not tell the actual path between two vertices.
The basic version can become slow if parent chains become long. Practical versions add path compression and union by rank or size.
Another common mistake is calling union(a, b) and assuming a becomes the root. The root choice is an implementation detail.
When To Use It
Use Union-Find for dynamic connectivity, cycle detection in undirected edge processing, Kruskal’s algorithm, and grouping problems where merges are frequent.
Do not use Union-Find for directed reachability or shortest path problems.
The Main Point
Union-Find represents each group by a root. Efficient versions make find paths short and merge smaller structures into larger ones.