Algorithm Search⌘K

Proofs: Union-find with rollback

These are the human proofs of Union-find with rollback, at full length.

Union-find with rollback restores the earlier forest on each undo full

This proof shows: correctness, termination.

For a node xx of a forest, T(x)T(x) is the set of nodes whose path to the root passes through xx, including xx. The sets of the structure are the sets T(r)T(r) of the roots rr. We prove that after each operation the invariants (S) and (H) of the page hold:

(S) The parent pointers form a forest, r.size=∣T(r)∣r.size = |T(r)| for each root rr, and 2∣T(x)∣≤∣T(x.p)∣2|T(x)| \le |T(x.p)| for each non-root xx.

(H) Let the stack be H=(h1,…,hk)H = (h_1, \ldots, h_k). There are forests F0,…,FkF_0, \ldots, F_k, with F0F_0 the forest of all elements as roots and FkF_k the current forest, such that for each ii: if hi=NONEh_i = \mathrm{NONE} then Fi=Fi−1F_i = F_{i-1}, and if hi=(a,b)h_i = (a, b) then aa and bb are roots of Fi−1F_{i-1} with a.size≤b.sizea.size \le b.size, and FiF_i is Fi−1F_{i-1} with a.p←ba.p \leftarrow b and b.size←b.size+a.sizeb.size \leftarrow b.size + a.size. Each FiF_i satisfies (S).

Here MAKE-SET adds a root to all of F0,…,FkF_0, \ldots, F_k, as if the element had been made at the start; this keeps every statement, because the new element is a root in each of them and appears in no record.

Depth bound. Under (S), if xx has depth dd, then the sizes along its path at least double at each step, so n≥∣T(root)∣≥2d∣T(x)∣≥2dn \ge |T(\mathrm{root})| \ge 2^d |T(x)| \ge 2^d and d≤⌊lg⁡n⌋d \le \lfloor \lg n \rfloor. So each FIND stops after at most ⌊lg⁡n⌋\lfloor \lg n \rfloor steps.

MAKE-SET. It adds a root xx with size 1 and T(x)={x}T(x) = \{x\}, so (S) holds, and (H) holds as explained above.

FIND(x)(x). Lines L2 and L3 write nothing, and by (S) they return the root of the tree of xx after at most ⌊lg⁡n⌋\lfloor \lg n \rfloor steps. So connected is right, and (S) and (H) still hold.

UNION(x,y)(x, y). Line L4 replaces xx and yy by their roots. If they are equal, line L5 pushes NONE and changes nothing, so (H) holds with Fk+1=FkF_{k+1} = F_k, and (S) holds. Otherwise, after line L6, xx and yy are different roots with x.size≤y.sizex.size \le y.size. Line L7 pushes (x,y)(x, y) and line L8 sets x.p=yx.p = y and adds the size, which is exactly the change that (H) describes for Fk+1F_{k+1}. We check (S) for the new forest. The parent pointers form a forest, because xx was a root of another tree, so no cycle is made. The new root yy has $|T(y)| = $ old ∣T(y)∣+∣T(x)∣=y.size+x.size|T(y)| + |T(x)| = y.size + x.size, which line L8 stores. The new edge satisfies 2∣T(x)∣=2 x.size≤x.size+y.size=∣T(y)∣2|T(x)| = 2\,x.size \le x.size + y.size = |T(y)|. For other non-roots zz, T(z)T(z) does not change, and T(z.p)T(z.p) grows or stays the same, so the inequality still holds. The sets T(x)T(x) and T(y)T(y) merge into one set, which is the effect that the disjoint-set type requires.

UNDO. Let k≥1k \ge 1 and pop hkh_k. If it is NONE, then Fk−1=FkF_{k-1} = F_k is the current forest, and (H) holds for the shorter stack with the same forests F0,…,Fk−1F_0, \ldots, F_{k-1}. If it is (a,b)(a, b), then by (H) the current forest FkF_k is Fk−1F_{k-1} with a.pa.p changed from aa to bb and b.sizeb.size increased by a.sizea.size. The value a.sizea.size in FkF_k equals its value in Fk−1F_{k-1}: sizes change only at the root that receives a link, and aa is not a root in FkF_k. So line L13, which subtracts a.sizea.size from b.sizeb.size and sets a.p←aa.p \leftarrow a, turns FkF_k into Fk−1F_{k-1} exactly. Then the current forest is Fk−1F_{k-1}, and (H) holds for the stack h1,…,hk−1h_1, \ldots, h_{k-1} with the forests F0,…,Fk−1F_0, \ldots, F_{k-1}. By (H), Fk−1F_{k-1} satisfies (S). The sets are those of Fk−1F_{k-1}, the state before the undone union, which is the effect that undo must have.

SNAPSHOT and ROLLBACK. A snapshot returns t=∣H∣t = |H|. Later operations push and pop, and as long as at least tt records remain, the first tt records and the forests F0,…,FtF_0, \ldots, F_t are the ones of the time of the snapshot, because a push or pop changes only the top of HH. So ROLLBACK(t)(t), which undoes until ∣H∣=t|H| = t, ends in the forest FtF_t of the snapshot, by the UNDO case applied ∣H∣−t|H| - t times.

Conclusion. By induction on the operations, (S) and (H) hold after each operation. So every find stops and returns the right root, each union merges the right sets, and each undo restores the forest and the sets as they were before the undone union. ■\blacksquare

Back to Union-find with rollback