Chapter 13 ยท Advanced Data Structures

Union-Find (Disjoint Sets)

Merge groups and ask "same group?" Compare quick-find with quick-union, then watch union by size/rank and path compression flatten the forest until each operation costs almost nothing.

Tip: click a node, then another node, to add union of the two. Click the same node twice to add find.

start node walked (pointer read) root found pointer rewritten roots being linked a root (set representative)
Operation log ยท cost of each operation

Why is it "almost constant"? The ฮฑ(n) intuition

Union by size alone keeps every tree at height โ‰ค log2 n: a node gets one level deeper only when its tree is hung under a tree at least as big, so its group at least doubles each time (Levitin ยง9.2). Path compression then makes every node on a walked path point straight at the root, so the next walk is short. Together, any sequence of m operations costs O(mยทฮฑ(n)), where ฮฑ is the inverse Ackermann function. It grows so slowly that ฮฑ(n) โ‰ค 4 for any n you could ever store.

A friendlier cousin is the iterated logarithm log* n: how many times you press "log2" before reaching 1. The easier bound O(log* n) per operation (Hopcroft and Ullman) is already tiny:

n241665,536265,536
log* n12345

Race every variant on a big input (no animation, just the totals). "Work" counts parent/id entries read while walking plus entries written, the same unit as the counters above.

How to read this

Try this

  1. Keep the lesson trace with union by size + path compression and run it. The parent array should end as 0 0 0 0 0 4 0 0: the final find(7) walks 7 โ†’ 6 โ†’ 4 โ†’ 0 and then rewires 7 and 6 straight to 0.
  2. Press Worst-case chain, choose Naive linking and switch compression off. The tree becomes a single path of height n โˆ’ 1, and each find(0) reads n entries. Now turn on compression: the first find is still expensive, the next ones cost 2 reads.
  3. Same chain, but Quick-find + Naive: every union relabels the whole growing group, 1 + 2 + โ€ฆ + (n โˆ’ 1) = n(n โˆ’ 1)/2 writes. Switch to union by size: each union relabels only one element.
  4. Turn on union by size with compression off and click nodes to build the tallest tree you can. You can't beat height log2 n (height 3 for n = 8, 4 for n = 16).
  5. In the race, compare "Quick-union naive" with "size + compression" on the chain workload: work per operation drops from about a thousand to about 3. Then try the random workload: naive quick-union still grows trees hundreds of levels tall, while size/rank keeps them to a handful of levels. Quick-find that relabels the smaller group is cheap here too, because each of its finds is a single read.

Go deeper