Memra

Disjoint sets & union-find

◈ 5 cards

MAKE-SET / UNION / FIND-SET; union by rank + path compression → O(m·α(n)); the engine of Kruskal.

The connectivity structure

A disjoint-set (union-find) structure maintains a collection of non-overlapping sets, each named by a representative member, and answers "are these two elements in the same set?" extremely fast. Three operations:

  • MAKE-SET(x) — create a singleton . .
  • UNION(x, y) — merge the sets containing and into one (destroying the originals).
  • FIND-SET(x) — return the representative of 's set.

The defining property: FIND-SET(u) == FIND-SET(v) iff and are in the same set. With MAKE-SET calls there can be at most UNIONs (each merge drops the set count by one). This is the workhorse behind connected components of a graph and, most importantly for this course, Kruskal's MST algorithm (Module 7): process edges cheapest-first, and add edge only when FIND-SET(u) != FIND-SET(v) (i.e. it joins two different components), then UNION them.

Disjoint-set forests and two heuristics

The efficient representation is a forest: each set is a rooted tree where every node points only to its parent, and the root (its own parent) is the representative. FIND-SET walks parent pointers up to the root; UNION links one root under another. Naively this can build a height- chain and make FIND-SET . Two heuristics fix it:

  1. Union by rank — each node keeps a rank (an upper bound on its height). In a union, the root of smaller rank points to the root of larger rank; on a tie, pick either and bump its rank. This keeps trees shallow, analogous to the weighted-union heuristic for lists.
  2. Path compression — during FIND-SET, make every node on the path point directly to the root. Since you walk to the root anyway, flattening is nearly free, and it makes future finds on those nodes . (Path compression never changes ranks — rank stays an upper bound on height, not the exact height.)

The running time: essentially linear

The bounds stack up:

  • Naive forest: per FIND-SET.
  • Union by rank alone: for operations.
  • Union by rank + path compression: — where is the inverse Ackermann function.

grows so absurdly slowly that for any up to the number of atoms in the observable universe (). So is "essentially linear" — effectively amortized per operation — even though, technically, it is superlinear (Tarjan proved is also a lower bound, so you cannot do strictly better).

Worked example — six elements, four unions

Start with MAKE-SET on — six singletons, six components. Now UNION(0,1), UNION(2,3), UNION(1,3), UNION(4,5):

  • UNION(0,1) merges and .
  • UNION(2,3).
  • UNION(1,3) merges the and components (because is now in the same tree as , and with ) → .
  • UNION(4,5).

Now FIND-SET(0) == FIND-SET(3) is True (both in ), but FIND-SET(0) == FIND-SET(5) is False ( vs ). Two components remain. Each FIND-SET after path compression points its path's nodes straight at the root, so repeated connectivity queries are effectively — this is exactly the "are these two vertices already connected?" test Kruskal runs on every edge.

0representative123find walks 2 links
After UNION(0,1), UNION(2,3), UNION(1,3): union by rank hung the root 2 under the root 0, so 3 is now two links from the representative and FIND-SET(3) has to walk both. The component {4,5} is a separate two-node tree.
0representative123now O(1) to find
FIND-SET(3) had to walk 3 → 2 → 0 anyway, so on the way back it repoints every node on that path directly at the root. Ranks are deliberately left alone — rank stays an upper bound on height, not the exact height. Compression plus union by rank is what gets you to O(m·α(n)).
NORMAL ~/memra/learn/comp-372/disjoint-sets-union-find utf-8 LF