Algorithm Search⌘K

Proofs: Left-child right-sibling representation

These are the human proofs of Left-child right-sibling representation, at full length.

The left-child right-sibling operations keep the chain invariant and traverse in preorder full

This proof shows: correctness.

We use the pseudocode of the page. The invariant is:

(S1) For each node pp with children c1,…,ckc_1, \ldots, c_k from left to right: if k=0k = 0 then $p.child = $ NIL; if k≥1k \ge 1 then p.child=c1p.child = c_1, ci.sibling=ci+1c_i.sibling = c_{i+1} for 1≤i<k1 \le i < k, and $c_k.sibling = $ NIL. (S2) ci.parent=pc_i.parent = p for each child cic_i of pp, and the root has no parent and no sibling.

A tree with one node, the root, meets (S1) and (S2) with all links NIL.

ADD-CHILD. Let pp have the children c1,…,ckc_1, \ldots, c_k, and let xx be a new node with no children, so $x.child = $ NIL. After the call, pp has the children x,c1,…,ckx, c_1, \ldots, c_k. Line L3 sets p.child=xp.child = x. Line L2 sets x.siblingx.sibling to the old p.childp.child, which by (S1) is c1c_1 if k≥1k \ge 1 and NIL if k=0k = 0. The links ci.siblingc_i.sibling do not change. So (S1) holds for pp. Line L1 sets x.parent=px.parent = p, so (S2) holds for xx. No other node changes, and xx has no children, so (S1) and (S2) hold for each node.

CHILDREN. By (S1), the sequence p.child,p.child.sibling,…p.child, p.child.sibling, \ldots is c1,c2,…,ckc_1, c_2, \ldots, c_k and then NIL. Lines L4 to L7 report exactly this sequence and stop after kk steps of line L7.

PREORDER. Write pre(x)\text{pre}(x) for the preorder of the subtree of xx: xx, then pre(c1),…,pre(ck)\text{pre}(c_1), \ldots, \text{pre}(c_k) for the children c1,…,ckc_1, \ldots, c_k of xx. For a node yy with right siblings y=y1,y2,…,yjy = y_1, y_2, \ldots, y_j (that is, yi+1=yi.siblingy_{i+1} = y_i.sibling and $y_j.sibling = $ NIL), write chain(y)=pre(y1)⋯pre(yj)\text{chain}(y) = \text{pre}(y_1) \cdots \text{pre}(y_j), the concatenation. By (S1), pre(x)=x⋅chain(x.child)\text{pre}(x) = x \cdot \text{chain}(x.child) when x.child≠x.child \ne NIL, and pre(x)=x\text{pre}(x) = x otherwise.

Define the pending sequence of a stack S=[s1,…,st]S = [s_1, \ldots, s_t], with sts_t on top, as seq(st)⋯seq(s1)\text{seq}(s_t) \cdots \text{seq}(s_1), where seq(r)=pre(r)\text{seq}(r) = \text{pre}(r) for the start node rr and seq(y)=chain(y)\text{seq}(y) = \text{chain}(y) for each other node. We claim that before each test of line L9,

(P) the nodes reported so far, followed by the pending sequence of SS, is pre(r)\text{pre}(r).

Initialization. Line L8 makes S=[r]S = [r] and nothing is reported. The pending sequence is pre(r)\text{pre}(r).

Maintenance. Let xx be the top of SS. Line L10 pops it and line L11 reports it.

  • If x=rx = r, then seq(x)=pre(r)=r⋅chain(r.child)\text{seq}(x) = \text{pre}(r) = r \cdot \text{chain}(r.child), or just rr if rr has no child. Line L12 pushes nothing. Line L13 pushes r.childr.child if it exists, with seq(r.child)=chain(r.child)\text{seq}(r.child) = \text{chain}(r.child).
  • If x≠rx \ne r, then seq(x)=chain(x)=pre(x)⋅chain(x.sibling)\text{seq}(x) = \text{chain}(x) = \text{pre}(x) \cdot \text{chain}(x.sibling), with the second factor empty if $x.sibling = $ NIL. And pre(x)=x⋅chain(x.child)\text{pre}(x) = x \cdot \text{chain}(x.child), with the second factor empty if $x.child = $ NIL. Line L12 pushes x.siblingx.sibling if it exists, and then line L13 pushes x.childx.child on top of it if it exists.

In each case, the reported xx followed by the pending sequence of the pushed nodes, top first, is seq(x)\text{seq}(x). The rest of the stack is unchanged. So (P) holds again.

Termination. Each iteration reports one node, and by (P) the reported nodes are a prefix of pre(r)\text{pre}(r), which has ss nodes, so there are at most ss iterations. The loop stops when SS is empty, and then the pending sequence is empty, so by (P) the reported sequence is pre(r)\text{pre}(r). □\square

The same argument, with post(x)=post(c1)⋯post(ck)⋅x\text{post}(x) = \text{post}(c_1) \cdots \text{post}(c_k) \cdot x and a mark on each node whose children were pushed, proves the postorder routine of the implementation.

Binary-tree view. Let BB be the binary tree on the same nodes with left(x)=x.child\text{left}(x) = x.child and right(x)=x.sibling\text{right}(x) = x.sibling. Then chain(y)\text{chain}(y) is the preorder of the subtree of yy in BB: by induction on the size of this subtree, its preorder is yy, then the preorder of the subtree of y.childy.child, then that of y.siblingy.sibling, which is pre(y)⋅chain(y.sibling)\text{pre}(y) \cdot \text{chain}(y.sibling) by the equation above. For the root, which has no sibling, this gives that the preorder of the tree is the preorder of BB.

Cost. Each line of ADD-CHILD is O(1)O(1). CHILDREN does O(1)O(1) work for each of the kk children, plus O(1)O(1). PREORDER does O(1)O(1) work in each of its at most ss iterations. □\square

Back to Left-child right-sibling representation