Home
Day 8 · Week 2

Basic Union-Find (Disjoint Set Union)

Level 3 · Union-Find  ·  2 hrs: ~45 min theory · ~60 min code · ~15 min notes

🧠 Mental Model

Union-Find maintains a collection of disjoint sets and answers one question fast: "are a and b in the same set?" — while letting you merge two sets. It's the tool for incremental connectivity: edges arrive one at a time and you must answer connectivity queries as you go.

Contrast with Day 4's component counting: BFS/DFS needs the whole graph up front and recomputes on change. Union-Find handles a stream of unions with near-constant cost per operation — which is why it underpins Kruskal's MST (Day 13) and countless "merge accounts / detect cycle" problems.

⚙️ The Forest Representation

Represent each set as a rooted tree; the root is the set's canonical representative. A single array parent[] encodes the whole forest: parent[x] is x's parent, and a root points to itself.

Invariant: every element reaches exactly one root by following parent[]; that root uniquely identifies its set.

Why "basic" is slow. With naive union (always attach a's root under b's), you can build a degenerate chain of length n, making find take O(n). That's the motivation for tomorrow's two optimizations.

📐 Correctness

Claim After any sequence of unions, find(a) == find(b) if and only if a and b have been connected by a chain of union operations.
Proof (invariant maintenance) Initially each element is its own tree (root = itself), so the claim holds — no unions, no connections. A union(a,b) only ever links the root of a's tree to the root of b's tree, merging exactly those two sets and no others. Thus the partition induced by "same root" always equals the partition induced by "connected via unions." Since find returns the root, the equivalence is preserved after every operation.

This is a classic equivalence-relation data structure: reflexive (x~x), symmetric (union is order-independent for connectivity), transitive (merging chains).

📊 Complexity (naive)

OperationTime (naive)Why
findO(n) worstDegenerate chain possible without balancing.
unionO(n) worstDominated by two finds.
spaceO(n)Single parent[] array.
Tomorrow (Day 9) union-by-rank + path compression drop this to O(α(n)) amortized — effectively constant. Today's goal is to get the structure and invariant rock-solid first.

💻 Reference Implementation (Java)

Deliberately un-optimized so the structure is clear.

/** Basic Union-Find — no rank, no path compression (yet). */
public class UnionFind {
    private final int[] parent;

    public UnionFind(int n) {
        parent = new int[n];
        for (int i = 0; i < n; i++) parent[i] = i;  // each its own root
    }

    /** Walk up to the root — the set's representative. */
    public int find(int x) {
        while (parent[x] != x) x = parent[x];
        return x;
    }

    /** Merge the two sets; returns false if already together. */
    public boolean union(int a, int b) {
        int ra = find(a), rb = find(b);
        if (ra == rb) return false;   // same set → nothing to do
        parent[ra] = rb;                // naive: attach ra under rb
        return true;
    }

    public boolean connected(int a, int b) { return find(a) == find(b); }
}

⚠️ Common Pitfalls

Linking elements, not roots: parent[a] = b instead of parent[find(a)] = find(b) corrupts the forest. Always union the roots.
Forgetting the same-root early return: harmless for correctness but matters when you use the boolean return to detect cycles (Day 10).
1-indexed inputs: size the array accordingly, or map to 0-based.

🎭 Problem Variances — where Union-Find hides

The tell is merging groups + connectivity queries, especially as relationships arrive incrementally. Even in "basic" form the pattern recognition is the same:

Disguise / phrasingWhat's really askedTwist to handle
"Provinces / friend circles" (static)Count componentsUnion all edges, count distinct roots
"Equations a==b, a!=b consistent?"Equivalence + contradiction checkUnion all '==' first, then verify '!=' pairs differ
"Similar strings / groups by relation"Grouping by transitive relationUnion when relation holds; group by root
"Are u and v connected?" (interleaved)Dynamic connectivityKeep one DSU alive; don't rebuild
"Two-set / bipartite via DSU"Enemy-of-enemy groupingExtended DSU with 2n nodes (self / opposite)
Recognition heuristic: "merge these, then ask who's together" = Union-Find. If edges stream in with queries between them, DSU beats re-running BFS every time.

🎯 Problems

LC 547 Number of Provinces Redo Day 4's problem with Union-Find; count distinct roots at the end. Med
LC 990 Satisfiability of Equality Equations Union all "==" pairs first, then verify no "!=" pair shares a root. Med

📚 References