Disjoint Set (Union-Find)

medium

Implement a Disjoint Set (Union-Find) over n elements 0 .. n-1:

  • union(a, b) — merge the sets containing a and b.
  • connected(a, b) — return whether a and b are in the same set.
  • components() — return the current number of disjoint sets.

Aim for near-O(1) amortized operations using union by rank/size and path compression.

Hints

Represent each set by a root; find walks to it, union links two roots.
connected(a, b) is find(a) == find(b); components starts at n and drops on each real union.
Path compression + union by rank keep the trees flat for near-O(1) operations.

Common doubts

They only reshape the internal trees; which elements share a set (and how many sets exist) is determined solely by the sequence of unions.
Both keep trees shallow and give the same near-O(1) guarantee; size (count of nodes) is often easier to maintain.
Unioning two elements already in the same set merges nothing, so the count is unchanged.

Interview follow-ups

It adds an edge only if its endpoints are in different sets (no cycle), unioning them otherwise.
Not directly — you'd need a rollback/persistent DSU that records and undoes unions.

Fun facts

  • With both optimizations, m operations cost O(m·α(n)) — α grows so slowly it's ≤ 4 for any practical n.
  • Union-Find is the classic example where a tiny tweak (compression) yields a dramatic speedup.

Asked at

AmazonGoogleMicrosoft
Frequently Sometimes Occasionally
Example 1
Input: DisjointSet(5), unite(0,1), unite(1,2), connected(0,2), components()
Output: null null null true 3
{0,1,2}, {3}, {4} — 0 and 2 share a set; three components remain.
Example 2
Input: DisjointSet(3), connected(0,1), unite(0,1), connected(0,1)
Output: null false null true
Initially separate, then merged.
Constraints

- 1 <= n <= 10^5 - 0 <= a, b < n - At most 2 * 10^4 operations

Solve this problem →