Algorithm Search⌘K

Proofs: Gomory-Hu tree

These are the human proofs of Gomory-Hu tree, at full length.

Gusfield's method builds a Gomory-Hu tree, and the queries read it correctly full

This proof shows: correctness, termination.

Let G=(V,E)G = (V, E) be undirected with V={0,…,n−1}V = \{0, \dots, n - 1\} and weights w≥0w \ge 0. For A⊆VA \subseteq V, c(A)c(A) is the total weight of edges with exactly one end in AA, so c(A)=c(V∖A)c(A) = c(V \setminus A), and λ(a,b)\lambda(a, b) is the least c(A)c(A) with a∈Aa \in A, b∉Ab \notin A. We use two facts proved on the page of the Gomory-Hu algorithm: submodularity, c(A)+c(B)≥c(A∩B)+c(A∪B)c(A) + c(B) \ge c(A \cap B) + c(A \cup B), and the triangle inequality, λ(a,c)≥min⁡(λ(a,b),λ(b,c))\lambda(a, c) \ge \min(\lambda(a, b), \lambda(b, c)).

Termination. BUILD runs the loop of line L2 exactly n−1n - 1 times; each iteration runs one maximum flow, which terminates, and a constant number of loops over the vertices. LIGHTEST ends because each iteration replaces the deeper of uu, vv by its parent, which lowers depth[u]+depth[v]depth[u] + depth[v], and the walk stops at their lowest common ancestor.

Subtrees. For a vertex k≠0k \ne 0, let DkD_k be the set of vertices whose chain of parents passes through kk, including kk.

Invariant. Before the iteration with ss (and at the end, with s=ns = n), call 0,…,s−10, \dots, s - 1 processed. Then: (a) the parent of every vertex other than 0 is processed and following parents from any vertex reaches 0; (b) for every processed k≠0k \ne 0, λ(k,parent[k])=weight[k]=c(Dk)\lambda(k, parent[k]) = weight[k] = c(D_k).

Read it as a tree TT whose nodes are the groups Nk={k}∪{j≥s:parent[j]=k}N_k = \{k\} \cup \{j \ge s : parent[j] = k\} for processed kk, with an edge (Nk,Nparent[k])(N_k, N_{parent[k]}) of weight weight[k]weight[k] and witness pair (k,parent[k])(k, parent[k]). The set DkD_k is then the union of the groups on the side of NkN_k when that edge is removed.

At the start only 0 is processed, every parent is 0, and (b) is empty. Assume the invariant before the iteration with ss, and let t=parent[s]t = parent[s], which is processed, so s∈Nt=:Xs \in N_t =: X. Let ZZ be the cut of line L4, a minimum ss-tt cut with s∈Zs \in Z, t∉Zt \notin Z.

The components of T−XT - X are: for each processed child kk of tt, the component with vertex set DkD_k and top vertex kk; and, if t≠0t \ne 0, the component containing parent[t]parent[t], with vertex set V∖DtV \setminus D_t and top vertex y=parent[t]y = parent[t]. Each is a minimum cut between its top vertex yy and a vertex xx of XX: for a child kk, DkD_k is a minimum kk-tt cut by (b); for the parent side, V∖DtV \setminus D_t is a minimum parent[t]parent[t]-tt cut by (b). Call this set DD in each case; it is disjoint from XX, contains yy, and does not contain x=tx = t.

Claim 1. There is a minimum ss-tt cut Z′Z' with Z′∩X=Z∩XZ' \cap X = Z \cap X such that for every component DD, D⊆Z′D \subseteq Z' if its top vertex y∈Zy \in Z, and D∩Z′=∅D \cap Z' = \emptyset otherwise. Proof. Treat the components one by one. If y∈Zy \in Z: the set Z∩DZ \cap D contains yy and not xx, so c(Z∩D)≥c(D)c(Z \cap D) \ge c(D); submodularity gives c(Z∪D)≤c(Z)c(Z \cup D) \le c(Z), and Z∪DZ \cup D contains ss and not tt, so it is again a minimum ss-tt cut. If y∉Zy \notin Z: the set Z∪(V∖D)Z \cup (V \setminus D) contains xx and not yy, so its value is at least c(D)=c(V∖D)c(D) = c(V \setminus D); submodularity gives c(Z∖D)≤c(Z)c(Z \setminus D) \le c(Z), and Z∖DZ \setminus D contains ss, not tt. Each step only adds or removes a whole DD, which is disjoint from XX and from the other components, so it does not change Z∩XZ \cap X, the membership of other top vertices, or components already treated. □\square

Claim 2. If the top vertex yy of a component DD lies in ZZ, then λ(s,y)=c(D)\lambda(s, y) = c(D). Proof. By Claim 1, D⊆Z′D \subseteq Z', so DD contains yy and not ss, and λ(s,y)≤c(D)\lambda(s, y) \le c(D). Let WW be a minimum ss-yy cut with y∈Wy \in W, s∉Ws \notin W. If t∉Wt \notin W, then WW separates tt from yy and c(W)≥λ(t,y)=c(D)c(W) \ge \lambda(t, y) = c(D). If t∈Wt \in W, then W∪(V∖Z′)W \cup (V \setminus Z') contains tt and not ss, so its value is at least c(Z′)=λ(s,t)c(Z') = \lambda(s, t), and submodularity gives c(W∩(V∖Z′))≤c(W)c(W \cap (V \setminus Z')) \le c(W); that set contains tt and not yy, so c(W)≥λ(t,y)=c(D)c(W) \ge \lambda(t, y) = c(D). Hence λ(s,y)=c(D)\lambda(s, y) = c(D). □\square

The update. Lines L5 and L6 set parent[v]←sparent[v] \leftarrow s for every v≠sv \ne s with parent[v]=tparent[v] = t and v∈Zv \in Z. For an unprocessed vv this moves it from the group of tt to the group of ss, so the new groups are Ns=X∩ZN_s = X \cap Z and Nt=X∖ZN_t = X \setminus Z. For a processed child kk, this moves the component DkD_k to hang below ss exactly when its top vertex kk lies in ZZ. Lines L7 and L8 do the same for the parent component: if parent[t]∈Zparent[t] \in Z, it hangs on ss instead of tt, and tt goes below ss. Now compute the new subtrees.

  • The edge between ss and tt. If there is no swap, parent[s]=tparent[s] = t and DsD_s is NsN_s together with the components below ss, which is Z′Z'. If there is a swap, parent[t]=sparent[t] = s and DtD_t is NtN_t together with the components on the side of tt, which is V∖Z′V \setminus Z'. Either way the edge has weight λ\lambda, which is λ(s,t)=c(Z′)=c(V∖Z′)\lambda(s, t) = c(Z') = c(V \setminus Z') by Claim 1, so (b) holds for it.
  • A child kk of tt that stays: DkD_k is unchanged, its witness (k,t)(k, t) is unchanged.
  • A child kk moved to ss: DkD_k is unchanged, and by Claim 2 λ(k,s)=c(Dk)=weight[k]\lambda(k, s) = c(D_k) = weight[k].
  • The parent edge after a swap: the edge is now (s,parent[s])(s, parent[s]) with parent[s]parent[s] the old parent[t]parent[t] and weight[s]weight[s] the old weight[t]weight[t]. Its subtree DsD_s is VV minus the parent component, the same set as the old DtD_t, so c(Ds)c(D_s) equals the old weight[t]weight[t], and by Claim 2, λ(s,parent[s])\lambda(s, parent[s]) equals it too. Without a swap the parent edge of tt is unchanged.
  • All other edges keep their subtrees.

After the update ss is processed, every parent is processed, and the parent chains still reach 0: the only changes point vertices at ss, whose own chain avoids them, or make ss the parent of tt while ss takes the old parent of tt. So the invariant holds before the next iteration.

The tree at the end. After the last iteration every vertex is processed and every group is a single vertex. For each edge (k,parent[k])(k, parent[k]), (b) says λ(k,parent[k])=weight[k]=c(Dk)\lambda(k, parent[k]) = weight[k] = c(D_k). Take u≠vu \ne v and the tree path u=x0,…,xr=vu = x_0, \dots, x_r = v. Each edge on the path has a subtree containing exactly one of uu, vv, so λ(u,v)\lambda(u, v) is at most its weight. By the triangle inequality along the path, λ(u,v)≥min⁡iλ(xi−1,xi)\lambda(u, v) \ge \min_i \lambda(x_{i-1}, x_i), the least edge weight on the path. So λ(u,v)\lambda(u, v) equals the least weight on the path, and the subtree of a lightest path edge, or its complement, is a minimum uu-vv cut.

The queries. LIGHTEST moves the deeper of uu and vv to its parent until they meet; the edges (u,parent[u])(u, parent[u]) it passes are exactly the edges of the tree path, each once, and it returns the lower end bb of a lightest one. MIN-CUT-VALUE returns weight[b]=λ(u,v)weight[b] = \lambda(u, v). MIN-CUT returns DbD_b or its complement, whichever holds uu; it does not hold vv, since the edge (b,parent[b])(b, parent[b]) lies on the path, and its value is c(Db)=weight[b]=λ(u,v)c(D_b) = weight[b] = \lambda(u, v). GLOBAL-MIN-CUT picks the lightest edge of the tree; every global cut separates some pair u,vu, v and so has value at least λ(u,v)\lambda(u, v), which is at least the lightest tree weight, and DbD_b attains it. ■\blacksquare

Back to Gomory-Hu tree