Question bank — Disjoint Set Union (Union-Find) — path compression + union by rank → α(n)
Warm-up picture 1 — a small DSU run you can trace by eye
Before the traps, pin down what the words mean by watching a tiny run. Below we do five unions on nodes {0..5}, drawing the forest after each. An arrow points from a child to its parent; a node with a self-loop (no arrow up) is a root. The little number beside a root is its rank.

Read the panels left-to-right. Notice the rule in action: when two roots of equal rank merge (panels 1, 2, 3) the survivor's rank ticks up by 1; when a lower-rank root joins a taller one (panel 5) nothing about rank changes — the short tree simply tucked inside the tall one's existing height. That single observation is the whole of "union by rank."
Warm-up picture 2 — what path compression does to a find
Now find(5) on the tree we just built. It climbs 5 → 4 → 0, discovers root 0, then re-points every node it passed straight at the root. The left panel is the tall "before", the right is the flattened "after".

The dashed orange arrows on the right are the new short-cuts. The next find(5) or find(4) is now a single hop. Crucially, rank[0] stays 2 even though the true height dropped to 1 — this is exactly why rank is only an upper bound, a fact several traps below hinge on.
Warm-up picture 3 — WHY a rank- tree owns at least nodes
Many True/False items lean on the claim "rank nodes ." Here is the whole inductive reason in one figure: a rank only rises from to when two rank- roots merge, so the new tree's size is the sum of two things each already .

Warm-up picture 4 — where α(n) comes from (schematic, not the full proof)
You do not need Tarjan's full potential-function proof, but you should see its shape so the α(n) traps make sense. First, two symbols we will use: let be the number of elements in the DSU and let be the number of operations (find/union calls) in the whole sequence we run. The idea: ranks partition into blocks by the Ackermann hierarchy (block boundaries at — each the previous tower of twos). A find climbs the tree; each step either crosses into a new block (few of these — at most , the number of blocks) or stays within a block, in which case path compression charges that step to the node itself so it can't be charged twice. Total work over all operations , i.e. amortized per operation.

True or false — justify
True or false: parent[x] == x is the only reliable way to test whether x is a root.
True or false: after path compression, the stored rank of a root equals the tree's true height.
find(5) flattened the tree but rank[0] stayed 2 while the real height fell to 1; rank is a (possibly loose) upper bound, and that staleness is intentional.True or false: union by rank guarantees every tree has height at most even without path compression.
True or false: path compression alone (no union by rank or by size) still gives amortized.
True or false: union by size and union by rank produce identical trees on the same input.
True or false: rank[x] is meaningful for a non-root node.
union); a non-root's rank is stale leftover data and must never be read.True or false: union(x, y) where x and y are already in the same set is a harmless no-op if you forget the early return.
if rx == ry: return you may do parent[rx] = rx (harmless) or bump a rank on a self-merge (corrupting the height bound), so the guard is mandatory.True or false: the inverse Ackermann is a true constant.
True or false: DSU can tell you how many connected components a graph has after processing all edges.
parent[x] == x); each root is exactly one component, which is why DSU underlies Connected Components.True or false: DSU can efficiently split a set back into two.
Spot the error
Error hunt: def find(x): return x if parent[x]==x else find(parent[x]) — what's missing?
parent[x] is never reassigned, so there's no compression (contrast picture 2's short-cuts) and the α(n) guarantee is lost; it must be parent[x] = find(parent[x]).Error hunt: in union, someone writes parent[rx] = ry right after ensuring rank[rx] >= rank[ry].
rx is the taller root, so the shorter ry must hang under rx (parent[ry] = rx); this line hangs the tall tree under the short one, wrecking the height bound.Error hunt: if rank[rx] == rank[ry]: rank[ry] += 1 after parent[ry] = rx.
ry is no longer a root, so raising its rank does nothing useful; the surviving root rx is the one whose rank must increment on a tie.Error hunt: a student updates rank[root] down to the true height after each find "to keep it accurate."
Error hunt: union(x, y) is called with raw x, y and immediately does parent[y] = x without any find.
find both endpoints first.Error hunt: someone initializes rank = [1]*n instead of [0]*n.
Why questions
Why does parent[x] = parent[parent[x]] (path halving) count as compression without full recursion?
Why does hanging the shorter tree under the taller one keep the combined height equal to the taller height?
Why does the height grow only when two ranks are equal?
Why is rank an upper bound rather than exact height even before any compression?
Why can we get away with never recomputing rank after compression?
Why do union by rank and path compression together beat either one alone?
Why does Kruskal use DSU instead of a plain visited-array?
same(x,y), which DSU answers in near-constant time. See Graph Cycle Detection.Why is (inverse Ackermann) the right function to describe the cost, and not or ?
Edge cases
Edge case: find(x) on a fresh singleton set — what happens?
parent[x] != x is immediately false, so it returns x in zero climbs; a lone node is its own root.Edge case: union(x, x) — same element twice.
find returns the same root for both, rx == ry, and the early return fires — a clean no-op, no rank change, no corruption.Edge case: merging two singletons (both rank 0).
Edge case: repeatedly union-ing every pair in one already-connected component.
rx == ry and returns instantly, so redundant unions cost only two finds and do nothing structural.Edge case: DSU over n nodes with zero unions performed.
n roots and n components — every node is its own singleton set, the identity state.Edge case: does the order of union calls change the final grouping of elements?
Recall One-line self-test
If someone hands you stale rank and a heavily compressed tree, can you still union correctly?
Yes ::: rank is only a comparison key for choosing the surviving root; correctness never needs it to equal true height.