Algorithm Search⌘K

Proofs: Reingold-Tilford rooted tree layout

These are the human proofs of Reingold-Tilford rooted tree layout, at full length.

The Reingold-Tilford layout is a tidy drawing and takes linear time full

This proof shows: correctness, termination.

Let TT be an ordered rooted tree with nn vertices and δ>0\delta > 0. For a vertex vv, the subtree TvT_v is drawn relative to vv when vv is at x=0x = 0 and each vertex uu of TvT_v is at xv(u)=∑rel(w)x_v(u) = \sum \mathrm{rel}(w) over the vertices w≠vw \ne v on the path from vv down to uu. Write Lv(i)L_v(i) for the set of vertices of TvT_v at depth ii below vv, and h(v)h(v) for the largest ii with Lv(i)≠∅L_v(i) \ne \emptyset.

Call a drawing of a forest tidy if, on each level, the vertices in preorder have increasing xx with gaps of at least δ\delta.

Lemma 1 (contours). When the loop of line L1 has handled vv, the values rel(c)\mathrm{rel}(c) are set for all vertices c≠vc \ne v of TvT_v, the drawing of TvT_v relative to vv is tidy, and lcv(i)=min⁡xv(Lv(i))\mathrm{lc}_v(i) = \min x_v(L_v(i)), rcv(i)=max⁡xv(Lv(i))\mathrm{rc}_v(i) = \max x_v(L_v(i)) for 0≤i≤h(v)0 \le i \le h(v).

Proof. By induction in the order of line L1, which handles each child before its parent. A leaf has one level with the single vertex at 00, so line L2 is right.

Let vv have the children c1,…,ckc_1, \ldots, c_k, each already handled. Consider the forest FjF_j made of Tc1,…,TcjT_{c_1}, \ldots, T_{c_j}, where TctT_{c_t} is drawn relative to ctc_t and shifted by off[ct]\mathrm{off}[c_t]. We show by induction on jj that FjF_j is tidy, in the preorder of the forest, and that after step jj the contours FF of the algorithm are the true smallest and largest xx on each level of FjF_j. For j=1j = 1, this is the induction hypothesis on c1c_1 with off[c1]=0\mathrm{off}[c_1] = 0 (line L3).

Step j≥2j \ge 2, with c=cjc = c_j. On a level ii that only one of Fj−1F_{j-1} and TcT_c has, the vertices of FjF_j on that level are those of one tidy part, so the level stays tidy. On a common level ii, the preorder of FjF_j lists the level of Fj−1F_{j-1} and then the level of TcT_c. Both parts are tidy, so the level of FjF_j is tidy exactly when the last vertex of the first part and the first vertex of the second part are at least δ\delta apart, which is

rcF(i)+δ≤lcc(i)+off[c].\mathrm{rc}_F(i) + \delta \le \mathrm{lc}_c(i) + \mathrm{off}[c].

Line L5 and line L6 choose off[c]=max⁡i(rcF(i)−lcc(i))+δ\mathrm{off}[c] = \max_i(\mathrm{rc}_F(i) - \mathrm{lc}_c(i)) + \delta, the least value for which this holds on all common levels (the level 00 is common, so the maximum exists). So FjF_j is tidy, and off[c]\mathrm{off}[c] is the leftmost tidy position, which is rule 4. On a common level, the smallest xx of FjF_j is that of Fj−1F_{j-1} and the largest is that of TcT_c plus off[c]\mathrm{off}[c]; on other levels both come from the part that has the level. This is the merge of line L7.

After the last child, line L8 sets m=off[ck]/2m = \mathrm{off}[c_k]/2, the midpoint of off[c1]=0\mathrm{off}[c_1] = 0 and off[ck]\mathrm{off}[c_k], and line L9 sets rel(cj)=off[cj]−m\mathrm{rel}(c_j) = \mathrm{off}[c_j] - m. So relative to vv, each vertex uu of TcjT_{c_j} has xv(u)=rel(cj)+xcj(u)x_v(u) = \mathrm{rel}(c_j) + x_{c_j}(u), which is its position in FkF_k minus mm. Shifting keeps FkF_k tidy, and adding vv alone on level 00 keeps the drawing tidy. The levels of TvT_v are level 00, holding vv at 00, and the levels of FkF_k one deeper, shifted by −m-m, which is line L10. □\square

Theorem. REINGOLD-TILFORD terminates and returns a drawing in which y(v)y(v) is the depth of vv, each level is tidy in preorder, each parent is at the midpoint of its first and last child, and each child subtree is at the leftmost tidy offset against the subtrees of its left siblings. No two edges cross.

Proof. Each loop runs over a finite list, so the algorithm stops. Lines L11 and L12 give y(v)y(v) equal to the depth and x(u)=xr(u)x(u) = x_r(u), the drawing of T=TrT = T_r relative to the root rr, since the preorder sets each parent before its children. By Lemma 1 for v=rv = r, every level is tidy. By lines L8 and L9, rel(c1)=−m\mathrm{rel}(c_1) = -m and rel(ck)=m\mathrm{rel}(c_k) = m, so the parent is at the midpoint of its first and last child. The offsets are leftmost by the proof of Lemma 1.

For the edges, take two edges (p,c)(p, c) and (p′,c′)(p', c') from level ii to level i+1i + 1 with cc before c′c' in preorder. Then pp is p′p' or comes before p′p' in preorder, because the preorder of the children follows the preorder of the parents. By tidiness, x(p)≤x(p′)x(p) \le x(p') and x(c)<x(c′)x(c) < x(c'). Two segments between the same two horizontal lines whose ends are in the same weak order on both lines can only meet at a shared end, which happens only when p=p′p = p'. So no two edges cross. □\square

Running time. Lines L2 to L4 and L8 to L12 do O(1)O(1) work per vertex or child, so O(n)O(n) in all. A merge of FF and TcT_c with the contour lists of the implementation (deepest level first, with a shift) costs O(1+min⁡(hF,hc))O(1 + \min(h_F, h_c)), where hFh_F, hch_c count levels. On each common level ii, the vertex ww of FF with the largest xx is, after the merge, to the left of a vertex of TcT_c on that level. In every larger subtree that contains FjF_j, that vertex of TcT_c stays to the right of ww on the same level, since later steps only shift whole subtrees and the relative order is the preorder. So ww is never again the rightmost vertex of its level, and each vertex is charged for at most one common level of one merge. There are at most n−1n - 1 merges, so all merges cost O(n)O(n), and the total time is Θ(n)\Theta(n) arithmetic operations. ■\blacksquare

Back to Reingold-Tilford rooted tree layout