Skip to main content

LeetCode Patterns

Union-Find Pattern

The Union-Find (Disjoint Set Union) pattern maintains a partition of N elements into non-overlapping groups, supporting two operations: find(x) returns the group representative, and union(x, y) merges two groups. With path compression and union-by-rank, both operations run in near-constant amortized time, formally O(alpha(N)) where alpha is the inverse Ackermann function.

By Alex Chen, Founder, InterviewChamp.AI · Last verified

Complexity

Time
O(alpha(N))
Space
O(N)

When to use Union-Find

  • The problem asks for the number of connected components, the size of the largest group, or whether two elements share a group.
  • Edges arrive one at a time and you need to detect when adding one would create a cycle (Kruskal's MST, Redundant Connection).
  • You need to merge sets dynamically and answer membership queries efficiently, where rebuilding the graph after every update would be too slow.
  • The problem can be reframed as equivalence classes — accounts that share an email, islands that touch, friends-of-friends groups.
  • An undirected graph is sparse and you want O(E * alpha(N)) instead of O(V + E) BFS/DFS, especially when edges stream in.

Template

class UnionFind {
  constructor(n) {
    this.parent = Array.from({ length: n }, (_, i) => i);
    this.rank = new Array(n).fill(0);
    this.count = n;
  }
  find(x) {
    if (this.parent[x] !== x) {
      this.parent[x] = this.find(this.parent[x]);
    }
    return this.parent[x];
  }
  union(x, y) {
    const rx = this.find(x);
    const ry = this.find(y);
    if (rx === ry) return false;
    if (this.rank[rx] < this.rank[ry]) [this.parent[rx], this.parent[ry]] = [ry, rx];
    else if (this.rank[rx] > this.rank[ry]) this.parent[ry] = rx;
    else { this.parent[ry] = rx; this.rank[rx]++; }
    this.count--;
    return true;
  }
}

Example LeetCode problems

#ProblemDifficultyFrequency
200Number of Islandsmediumfoundational
323Number of Connected Components in an Undirected Graphmediumfoundational
684Redundant Connectionmediumfrequently asked
721Accounts Mergemediumcompany favorite
261Graph Valid Treemediumclassic
547Friend Circles (Number of Provinces)mediumfrequently asked
947Most Stones Removed with Same Row or Columnmediumcompany favorite

Common mistakes

  • Implementing find() without path compression, which degrades the worst-case operation cost to O(N) and causes TLE on tight time limits.
  • Implementing union() without union-by-rank or union-by-size, producing chain-shaped trees that defeat path compression's amortized bound.
  • Forgetting to call find() on both operands before comparing them in union() — using raw parent[x] gives the immediate parent, not the root.
  • On Accounts Merge, indexing the union-find by account name rather than by a stable email-to-index map, which causes silent merges across distinct people with the same name.
  • Decrementing the component count even when the two endpoints already share a root, which understates the final component count.

Frequently asked questions

When should I use Union-Find vs BFS/DFS?

Use Union-Find when edges arrive incrementally and you need to answer connectivity or cycle-detection queries between updates. Use BFS/DFS when the graph is given upfront and you walk it once. Union-Find is also the standard data structure for Kruskal's minimum spanning tree.

How does path compression actually work?

During find(x), after locating the root, point every node along the path directly to the root. The next find() on any of those nodes returns in one step. Combined with union-by-rank, this drops the amortized cost per operation to O(alpha(N)) — effectively constant for any practical N.

Can Union-Find handle deletions or edge removals?

Not natively — it's a build-up data structure. To support deletions you either rebuild after each deletion (expensive) or process the operations offline in reverse, adding edges from last to first while answering queries about the final state.

Why does Redundant Connection use Union-Find instead of cycle detection by DFS?

Both work, but Union-Find is cleaner: process edges one by one, and the first edge whose endpoints already share a root is the redundant one. DFS would require a re-traversal after each edge add. The Union-Find solution is O(E * alpha(N)) and reads in a few lines.

Solve it now

Free. No sign-up. Python and JavaScript run instantly in your browser.

Output

Press Run or Cmd+Enter to execute

Practice these live with InterviewChamp.AI

Drill the Union-Find pattern under real interview conditions with instant feedback on your reasoning, complexity claims, and code.

Practice these live with InterviewChamp.AI →