Algorithm Search⌘K

Proofs: Link-cut tree

These are the human proofs of Link-cut tree, at full length.

The operations of a link-cut tree keep the preferred path invariant full

This proof shows: correctness.

We use invariant (L) of the page. Each real tree is rooted. For a splay tree SS, write seq(S)\mathrm{seq}(S) for its symmetric order with all pending reversal bits applied: a bit on a vertex reverses the sequence of its whole subtree. (L1) says that seq(S)\mathrm{seq}(S) is a vertical path x1,…,xkx_1, \ldots, x_k of the real tree, with xi+1x_{i+1} a child of xix_i. (L2) says that the parent pointer of the splay root of SS is the real parent of x1x_1, or nothing if x1x_1 is the root of its real tree. (L3) says that each stored sum and maximum is that of the values in the splay subtree. Each vertex lies in exactly one splay tree.

Lemma 1 (splaying). Pushing a reversal bit and rotating inside a splay tree change neither seq(S)\mathrm{seq}(S) nor the set of its vertices, and they keep (L3). After SPLAY(x)(x), xx is the splay root and its parent pointer is the old path-parent of SS.

Proof. Pushing the bit of yy swaps the children of yy and toggles the bits of both children. The sequence of the subtree of yy was the reverse of seq(left) y seq(right)\mathrm{seq}(\text{left})\,y\,\mathrm{seq}(\text{right}), with the children's sequences read with their own bits; after the push it is rev(seq(right)) y rev(seq(left))\mathrm{rev}(\mathrm{seq}(\text{right}))\,y\,\mathrm{rev}(\mathrm{seq}(\text{left})), which is the same sequence. SPLAY pushes the bits on the path from the splay root to xx before any rotation, so every rotation acts on vertices without pending bits, where it keeps the symmetric order as in any binary search tree. Each rotation recomputes the aggregates of the two rotated vertices from their new children, which keeps (L3). In a rotation at the splay root pp, the new splay root xx takes the parent pointer of pp, so the path-parent is kept. □\square

Lemma 2 (access). After ACCESS(v)(v), (L) holds, the splay tree of vv holds exactly the path from the root of the real tree to vv, and vv is its splay root with no right child.

Proof. Let the loop visit x0=v,x1,x2,…,xtx_0 = v, x_1, x_2, \ldots, x_t, where xi+1x_{i+1} is the parent pointer of xix_i after the splay of xix_i. We show by induction on ii that after the step at xix_i (lines L3 to L5), (L) holds, and the splay tree of xix_i holds the path from the top of the old path of xix_i down to vv, ordered by depth.

For i=0i = 0, after line L3 vv is the splay root of its path PP by Lemma 1. Its right subtree holds the vertices of PP below vv. Line L4 makes that subtree a separate splay tree. Its sequence is the part of PP below vv, a vertical path whose top is a child of vv, and its root keeps the parent pointer vv, so (L2) holds for it. The tree of vv now holds the part of PP from its top to vv. The aggregates of vv are recomputed, so (L3) holds.

For i>0i > 0, the splay root xi−1x_{i-1} of the tree SS built so far has parent pointer xix_i, which by (L2) is the real parent of the top of seq(S)\mathrm{seq}(S). So the path in SS starts at a child of xix_i. After line L3, xix_i is the root of its splay tree, whose sequence is a path QQ through xix_i. Line L4 makes the part of QQ below xix_i a separate splay tree with path-parent xix_i, as in the base case, and makes SS the right subtree of xix_i. The sequence of the tree of xix_i is now the part of QQ from its top to xix_i, then seq(S)\mathrm{seq}(S), which is a vertical path ordered by depth because the top of seq(S)\mathrm{seq}(S) is a child of xix_i. Line L4 also sets the parent of xi−1x_{i-1} to xix_i, now as a splay edge, and the aggregates of xix_i are recomputed.

The loop ends when the parent pointer is nothing, so by (L2) the top of the last path is the root of the real tree. Then the splay tree of vv holds the path from the root to vv. The last splay, line L6, moves vv to the splay root; since vv is the deepest vertex of the sequence, it has no right child. □\square

Lemma 3 (make root). After MAKE-ROOT(v)(v), (L) holds for the same tree rooted at vv.

Proof. Let r=y1,y2,…,yk=vr = y_1, y_2, \ldots, y_k = v be the path from the old root to vv. Rooting at vv instead of rr changes the parent of yiy_i from yi−1y_{i-1} to yi+1y_{i+1} for i<ki < k, makes vv parentless, and keeps every other parent. By Lemma 2 the splay tree of vv holds exactly y1,…,yky_1, \ldots, y_k, and line L8 reverses its sequence to yk,…,y1y_k, \ldots, y_1, which is a vertical path of the new rooted tree, top first. Its top vv has no parent, and the parent pointer of vv is nothing, as (L2) needs. Every other splay tree has a top xx that is not on the path (each yiy_i is in the tree of vv), so the parent of xx is unchanged, and its sequence is a path that avoids y1,…,yky_1, \ldots, y_k except possibly at its parent, so its parent relations are unchanged. A reversal bit does not change any aggregate of a sum or a maximum. □\square

Theorem. After each operation, (L) holds for the current forest, and each query returns the value of its definition.

Proof. At the start every vertex is a splay tree of its own with no parent pointer, which satisfies (L). Lemmas 1 to 3 cover splay, access and make root.

Find root (lines L9 and L10). After the access, the splay tree of vv holds the path from the real root to vv, and its leftmost vertex in the symmetric order with bits applied is the first vertex of the sequence, the real root. The walk pushes bits on its way down, so it reads the right children. The final splay keeps (L) by Lemma 1.

Connected. Two vertices are in the same tree if and only if they have the same real root.

Link(u,v)(u, v) (lines L11 and L12) with uu and vv in different trees. After MAKE-ROOT(u)(u), by Lemmas 2 and 3, uu is the real root of its tree and the splay root of a tree whose sequence starts at uu. The access of vv keeps (L) and does not touch the tree of uu. In the new forest, vv is the parent of uu and all other parents are kept. Setting the parent pointer of uu to vv makes (L2) hold for the splay tree of uu, whose top is uu; the other splay trees keep their tops and parents.

Cut(u,v)(u, v) (lines L13 and L14) with {u,v}\{u, v\} a forest edge. After MAKE-ROOT(u)(u) and ACCESS(v)(v), by Lemma 2 the splay tree of vv holds the path from the root uu to vv, which is u,vu, v, with vv as splay root without a right child. So the tree has exactly two vertices and uu is the left child of vv. Line L14 makes uu a splay tree of its own with no parent pointer: uu is the real root of its new tree, as (L2) needs. The splay tree of vv is now {v}\{v\}, with no parent pointer, and vv is the root of its new tree. Every other splay tree keeps its top, and the real parent of each top is unchanged, since only the edge between uu and vv was removed. The aggregates of vv are recomputed.

Path sum and path maximum (lines L15 and L16). After MAKE-ROOT(u)(u) and ACCESS(v)(v), by Lemma 2 the splay tree with root vv holds exactly the path from uu to vv, and by (L3) the aggregates at vv are those of this path.

Set value accesses vv, so vv is a splay root, and recomputes only the aggregates of vv, which are the only ones that contain its value.

Lowest common ancestor. After MAKE-ROOT(r)(r) and ACCESS(u)(u), the splay tree RR that holds the root rr has the sequence of the path from rr to uu. Run the loop of an access of vv. Every step before the last one splays a vertex whose splay tree does not contain rr, so these steps do not change RR; the last step visits a vertex xx of RR, since the loop ends at the splay tree that contains the real root. By the induction in Lemma 2, xx is an ancestor of vv (or vv itself), and xx lies on the path from rr to uu, so xx is a common ancestor. The vertex below xx on the path from xx to vv, if any, is the top of a different preferred path, so it is not on the path from rr to uu. Hence no deeper common ancestor exists, and xx is the lowest common ancestor. ■\blacksquare

Back to Link-cut tree