Pattern Guide
Union-Find Applications
"Connect components dynamically. Detect cycles, count components, earliest connection."
Union-Find (Disjoint Set Union) efficiently answers: are two elements in the same component? It supports union (merge components) and find (get component representative) in near-O(1) amortized with path compression and union by rank. Applications: count connected components, detect cycles in undirected graphs, Kruskal's MST, earliest time when all connected, account merging.
Problems you can solve with this pattern
4 problems · click any to start solving
class UnionFind {
constructor(n) {
this.parent = Array.from({length: n}, (_, i) => i);
this.rank = new Array(n).fill(0);
this.count = n; // number of components
}
find(x) {
if (this.parent[x] !== x) this.parent[x] = this.find(this.parent[x]); // path compression
return this.parent[x];
}
union(x, y) {
const px = this.find(x), py = this.find(y);
if (px === py) return false; // already connected
// Union by rank
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]++; }
this.count--;
return true;
}
connected(x, y) { return this.find(x) === this.find(y); }
}Union-Find with path compression and union by rank: find(x) = parent[x] == x ? x : parent[x] = find(parent[x]). Union(x, y): link smaller rank to larger. Track component count: decrement on each successful union. Cycle detection: union returns false if both already in same component. Weighted union-find: store weight (ratio, difference) at each edge for path queries.
class UnionFind {
constructor(n) {
this.parent = Array.from({length: n}, (_, i) => i);
this.rank = new Array(n).fill(0);
this.count = n; // number of components
}
find(x) {
if (this.parent[x] !== x) this.parent[x] = this.find(this.parent[x]); // path compression
return this.parent[x];
}
union(x, y) {
const px = this.find(x), py = this.find(y);
if (px === py) return false; // already connected
// Union by rank
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]++; }
this.count--;
return true;
}
connected(x, y) { return this.find(x) === this.find(y); }
}When to use Union-Find vs BFS/DFS:
- Dynamic connectivity (edges added over time): Union-Find
- Static graph, one-time query: BFS/DFS fine
- Need path between nodes: BFS/DFS
- Just need same-component check: Union-Find
Rollback Union-Find: Without path compression, use union by rank only — then union/undo is O(log n). Used in offline algorithms.