Algorithm Search⌘K

Proofs: Hopcroft-Ullman bound

These are the human proofs of Hopcroft-Ullman bound, at full length.

Union by rank with path compression runs in O(m log* n) time full

This proof shows: statement.

We prove that a sequence of mm operations MAKE-SET, UNION and FIND, of which nn are MAKE-SET, on a disjoint-set forest with union by rank and path compression takes O(mlg⁡∗n)O(m \lg^* n) time. At the end we show that the same proof works for union by size.

Setup. A UNION is two FINDs and one LINK of two roots, so the sequence becomes at most 3m3m operations MAKE-SET, FIND and LINK. MAKE-SET and LINK take O(1)O(1) time, so they cost O(m)O(m) in all. A FIND on the path x1,x2,…,xsx_1, x_2, \ldots, x_s, where xj+1=xj.px_{j+1} = x_j.p and xsx_s is the root, takes O(s)O(s) time. We count one unit for each node xjx_j with j<sj < s of each FIND, plus one unit per FIND; this is the time of the FINDs up to a constant factor.

Rank facts. From the union by rank page:

  1. For each non-root xx, x.rank<x.p.rankx.rank < x.p.rank. So ranks increase strictly along each path.
  2. The rank of a node changes only while it is a root. Once a node is a non-root, it never becomes a root again, and its rank is fixed.
  3. Each rank is at most ⌊lg⁡n⌋\lfloor \lg n \rfloor.
  4. For each r≥0r \ge 0, at most n/2rn/2^r nodes ever have rank rr.

Blocks. Define t(0)=1t(0) = 1 and t(g)=2t(g−1)t(g) = 2^{t(g-1)} for g≥1g \ge 1, so t(1)=2t(1) = 2, t(2)=4t(2) = 4, t(3)=16t(3) = 16, t(4)=65536t(4) = 65536. Put rank rr in block B(r)=0B(r) = 0 if r≤1r \le 1, and in block B(r)=gB(r) = g if t(g−1)<r≤t(g)t(g - 1) < r \le t(g) for g≥1g \ge 1. Each rank r≥0r \ge 0 is in exactly one block, and BB is non-decreasing in rr.

Number of blocks. Let G=lg⁡∗nG = \lg^* n. We show that each rank r≤⌊lg⁡n⌋r \le \lfloor \lg n \rfloor has B(r)≤GB(r) \le G. If n≤2n \le 2 then r≤1r \le 1 and B(r)=0B(r) = 0. Otherwise, lg⁡∗n=G\lg^* n = G means that GG applications of lg⁡\lg take nn to a value at most 1, so n≤t(G)n \le t(G), since lg⁡\lg applied GG times to t(G)t(G) gives t(0)=1t(0) = 1, and lg⁡\lg is increasing. Then lg⁡n≤lg⁡t(G)=t(G−1)\lg n \le \lg t(G) = t(G - 1), so r≤t(G−1)≤t(G)r \le t(G - 1) \le t(G) and B(r)≤GB(r) \le G. So all ranks lie in the G+1G + 1 blocks 0,1,…,G0, 1, \ldots, G. Write B(x)B(x) for B(x.rank)B(x.rank).

Charging. Consider one FIND on the path x1,…,xsx_1, \ldots, x_s, before the compression, and a node xjx_j with j<sj < s, so xjx_j is not the root. Charge the unit of xjx_j:

  • to the FIND, if xj.px_j.p is the root (j=s−1j = s - 1), or if B(xj.p)>B(xj)B(x_j.p) > B(x_j);
  • to the node xjx_j, otherwise, that is, if xj.px_j.p is not the root and B(xj.p)=B(xj)B(x_j.p) = B(x_j).

By fact 1, B(xj.p)≥B(xj)B(x_j.p) \ge B(x_j) always, so these cases cover all nodes.

Charges to a FIND. The units charged to one FIND are its own unit, at most one for xs−1x_{s-1}, and one for each jj with B(xj+1)>B(xj)B(x_{j+1}) > B(x_j). Along the path the blocks B(x1)≤B(x2)≤⋯≤B(xs)B(x_1) \le B(x_2) \le \cdots \le B(x_s) do not decrease and lie in 0,…,G0, \ldots, G, so they increase at most GG times. So one FIND has at most G+2G + 2 charges, and all FINDs together at most 3m(G+2)3m(G + 2).

Charges to a node. Fix a node xx. Its rank does not change once it is a non-root (fact 2), and it is charged only as a non-root, so B(x)B(x) is fixed during all its charges. Let g=B(x)g = B(x).

Each time xx is charged, in a FIND with x=xjx = x_j, its parent y=xj+1y = x_{j+1} is not the root, so the root xsx_s is a proper ancestor of yy, and xs.rank>y.rankx_s.rank > y.rank by fact 1. Path compression sets x.p←xsx.p \leftarrow x_s. So after each charge, x.p.rankx.p.rank is larger than before the charge. Between charges, x.p.rankx.p.rank does not fall: the parent of xx changes only by compression, to a proper ancestor of the old parent, which has a larger rank, and the old parent, if it stays, keeps or raises its rank.

So the ranks of the parent of xx at its successive charges strictly increase. At each charge, B(x.p)=gB(x.p) = g, so x.p.rank≤t(g)x.p.rank \le t(g); and x.p.rank>x.rank≥0x.p.rank > x.rank \ge 0. A strictly increasing sequence of integers in [1,t(g)][1, t(g)] has at most t(g)t(g) terms. So xx is charged at most t(g)t(g) times. In block 0 this gives at most t(0)=1t(0) = 1 charge.

Charges to the nodes of one block. Let N(g)N(g) be the number of nodes xx with B(x)=gB(x) = g at the time they are charged; by fact 2 each node is counted in at most one block. For g=0g = 0, N(0)≤nN(0) \le n, so block 0 takes at most n⋅t(0)=nn \cdot t(0) = n charges. For g≥1g \ge 1, by fact 4,

N(g)≤∑r=t(g−1)+1∞n2r=n2t(g−1)=nt(g).N(g) \le \sum_{r = t(g-1) + 1}^{\infty} \frac{n}{2^r} = \frac{n}{2^{t(g-1)}} = \frac{n}{t(g)}.

So the nodes of block gg take at most N(g)⋅t(g)≤nN(g) \cdot t(g) \le n charges. Over the G+1G + 1 blocks, the nodes take at most n(G+1)n(G + 1) charges.

Total. Every unit of every FIND is charged once, to the FIND or to a node. So the FINDs cost at most 3m(G+2)+n(G+1)3m(G + 2) + n(G + 1) units, and the whole sequence costs

O(m)+3m(lg⁡∗n+2)+n(lg⁡∗n+1)=O(mlg⁡∗n),O(m) + 3m(\lg^* n + 2) + n(\lg^* n + 1) = O(m \lg^* n),

because n≤mn \le m. ■\blacksquare

Union by size. With union by size, define for a non-root xx the number ρ(x)=⌊lg⁡s(x)⌋\rho(x) = \lfloor \lg s(x) \rfloor, where s(x)s(x) is the size of the tree of xx at the moment it was linked below another root, and for a root ρ(x)=⌊lg⁡(the size of its tree)⌋\rho(x) = \lfloor \lg (\text{the size of its tree}) \rfloor. We check facts 1 to 4 for ρ\rho in place of rank; the proof above uses nothing else.

Fact 2: s(x)s(x) is fixed once xx is a non-root, and a root's size only grows. Fact 3: ρ(x)≤⌊lg⁡n⌋\rho(x) \le \lfloor \lg n \rfloor. Fact 1: when xx is linked below the root yy, union by size gives ∣T(y)∣≥s(x)|T(y)| \ge s(x), so the new tree of yy has at least 2s(x)2 s(x) nodes. Every later ancestor zz of xx has yy in its tree, so the size that zz has when it stops being a root, or its present size if it is a root, is at least the size of the tree of yy after this link, which is at least 2s(x)2s(x). So ρ(z)≥⌊lg⁡(2s(x))⌋=ρ(x)+1\rho(z) \ge \lfloor \lg (2 s(x)) \rfloor = \rho(x) + 1. Compression only gives xx new parents that are ancestors, so ρ\rho increases strictly along each path. Fact 4: let x≠x′x \ne x' both be non-roots with ρ=r\rho = r. Their trees at the time of their links have at least 2r2^r nodes each. If one of these trees contained the other node, say x′x' in the tree of xx, then xx is an ancestor of x′x' and ρ(x)>ρ(x′)\rho(x) > \rho(x') by fact 1, a contradiction. The trees of a union history are nested or disjoint, so the two trees are disjoint. So at most n/2rn/2^r non-roots have ρ=r\rho = r. Roots are never charged, and the counting of N(g)N(g) is only over charged nodes, which are non-roots. So the proof holds for union by size, which is the form of Hopcroft and Ullman.

Back to Hopcroft-Ullman bound