Algorithm Search⌘K

Proofs: Connectivity by union-find

These are the human proofs of Connectivity by union-find, at full length.

Connectivity by union-find stops and labels the connected components full

This proof shows: correctness, termination.

Let V={0,…,n−1}V = \{0, \ldots, n-1\} and let e1,…,eme_1, \ldots, e_m be the edges in the order line L2 reads them. Self-loops and repeated edges are allowed. Let Gi=(V,{e1,…,ei})G_i = (V, \{e_1, \ldots, e_i\}), and write x≡iyx \equiv_i y when GiG_i has a path from xx to yy. This is an equivalence relation, and its classes are the components of GiG_i.

For a vertex xx, the chain of xx is x,parent[x],parent[parent[x]],…x, parent[x], parent[parent[x]], \ldots. A vertex rr with parent[r]=rparent[r] = r is a root.

Lemma 1 (ranks). At every moment, each vertex xx that is not a root has rank[x]<rank[parent[x]]rank[x] < rank[parent[x]]. So the chain of each vertex reaches a root after at most n−1n - 1 steps, and the loops of lines L7 and L8 stop.

Proof. After line L1 every vertex is a root, so the claim holds. Only lines L8, L11 and L12 change parentparent or rankrank.

  • Line L11 sets parent[b]←aparent[b] \leftarrow a, where aa and bb are distinct roots and, after line L10, rank[a]≥rank[b]rank[a] \ge rank[b]. If rank[a]>rank[b]rank[a] > rank[b] the claim holds for bb. If they are equal, line L12 raises rank[a]rank[a] by 1, and then rank[b]<rank[a]rank[b] < rank[a].
  • Line L12 raises only the rank of the root aa. For a child cc of aa, rank[c]<rank[a]rank[c] < rank[a] stays true. The ranks of non-roots never change, because lines L11 and L12 change only the ranks of roots and bb stops being a root after its rank is fixed.
  • Line L8 sets parent[x]←rparent[x] \leftarrow r, where rr is the root that line L7 found at the end of the chain of xx. Along the chain the ranks strictly increase, by the claim before the step, so rank[x]<rank[r]rank[x] < rank[r].

So the claim holds at every moment. Along a chain the ranks strictly increase. A rank is at most n−1n - 1, because it grows only in line L12, which runs at most once for each LINK, and there are at most n−1n - 1 LINK calls (each one makes a root into a non-root, and a non-root never becomes a root again). So a chain has at most nn vertices, and line L7 stops. Each step of line L8 moves xx one place forward on its own chain toward rr, so line L8 stops after at most n−1n - 1 steps. □\square

Write ρ(x)\rho(x) for the root at the end of the chain of xx. Then FIND(x)\mathrm{FIND}(x) returns ρ(x)\rho(x), by line L7.

Lemma 2 (FIND keeps the roots). A call of FIND does not change ρ(y)\rho(y) for any vertex yy.

Proof. Line L8 changes parent[x]parent[x] only for vertices xx on the chain of the argument, all of which have ρ(x)=r\rho(x) = r, and it sets parent[x]=rparent[x] = r. Take any yy. If its chain does not meet a changed vertex, it is the same as before. Else let xx be the first changed vertex on it. Before the change, the chain of yy continued from xx to rr, and after it, it goes from xx to rr in one step. In both cases it ends at rr. □\square

Lemma 3 (LINK merges two classes). Let a≠ba \ne b be roots. After LINK(a,b)(a, b), ρ(y)\rho(y) is the new parent root for each yy that had ρ(y)∈{a,b}\rho(y) \in \{a, b\} before, and ρ(y)\rho(y) is unchanged for every other yy.

Proof. After line L10 let the roots be named so that line L11 sets parent[b]←aparent[b] \leftarrow a. This is the only change of a parent. A chain that ended at bb now continues to aa, which is still a root. A chain that ended at aa or at another root does not pass through bb, because bb was a root and a chain stops at its root, so it is unchanged. □\square

Theorem. UF-COMPONENTS stops, and it returns compcomp with comp[x]=comp[y]comp[x] = comp[y] if and only if xx and yy are joined by a path in G=GmG = G_m.

Proof. Every loop stops by Lemma 1, and the loops of lines L2 and L5 have mm and nn steps. We show by induction on ii the invariant

(I) After the first ii steps of the loop of line L2, ρ(x)=ρ(y)\rho(x) = \rho(y) if and only if x≡iyx \equiv_i y.

Base. For i=0i = 0, every vertex is its own root, and G0G_0 has no edges, so x≡0yx \equiv_0 y exactly when x=yx = y.

Step. Let ei={u,v}e_i = \{u, v\}, and assume (I) for i−1i - 1. Line L3 computes a=ρ(u)a = \rho(u) and b=ρ(v)b = \rho(v), and by Lemma 2 it does not change any ρ\rho.

First, we compare ≡i\equiv_i with ≡i−1\equiv_{i-1}. Each path of Gi−1G_{i-1} is a path of GiG_i. A walk of GiG_i that uses eie_i goes from one end of eie_i to the other at that place. So x≡iyx \equiv_i y if and only if x≡i−1yx \equiv_{i-1} y, or x≡i−1ux \equiv_{i-1} u and v≡i−1yv \equiv_{i-1} y, or x≡i−1vx \equiv_{i-1} v and u≡i−1yu \equiv_{i-1} y. In words: the classes of uu and vv in Gi−1G_{i-1} merge, and every other class stays as it is.

If a=ba = b, then u≡i−1vu \equiv_{i-1} v by (I), the two classes are one, and ≡i\equiv_i equals ≡i−1\equiv_{i-1}. Line L4 does nothing, so (I) holds for ii. This case covers self-loops and repeated edges. If a≠ba \ne b, the classes of uu and vv in Gi−1G_{i-1} are the sets {y:ρ(y)=a}\{y : \rho(y) = a\} and {y:ρ(y)=b}\{y : \rho(y) = b\}, by (I). Line L4 calls LINK(a,b)(a, b), and by Lemma 3 exactly these two sets get one common root, while every other vertex keeps its root. This is the same merge as the one of ≡i\equiv_i, so (I) holds for ii.

End. After the loop, (I) holds with i=mi = m. Line L5 sets comp[x]=FIND(x)=ρ(x)comp[x] = \mathrm{FIND}(x) = \rho(x), and by Lemma 2 the calls of line L5 do not change any ρ\rho, so all labels are taken from the same root function. So comp[x]=comp[y]comp[x] = comp[y] if and only if x≡myx \equiv_m y. ■\blacksquare

Spanning forest. Let TT be the set of edges that caused a LINK. Each such edge joins two different classes of ≡i−1\equiv_{i-1}, so it closes no cycle with the earlier edges of TT, and TT is a forest. The same induction shows that TT and GiG_i have the same classes after each step, so TT is a spanning forest of GG with n−cn - c edges, where cc is the number of components.

Back to Connectivity by union-find