Algorithm Search⌘K

Proofs: Inorder traversal

These are the human proofs of Inorder traversal, at full length.

The stack-based inorder traversal lists each node once, in inorder full

This proof shows: correctness, termination.

Let the binary tree have the nodes 0,…,n−10, \ldots, n-1 and the root rr, or be empty with r=NILr = \mathrm{NIL}. For a node xx let T(x)T(x) be the set of nodes in the subtree of xx, with T(NIL)=∅T(\mathrm{NIL}) = \emptyset, and let In(x)In(x) be the inorder list of T(x)T(x): In(NIL)=( )In(\mathrm{NIL}) = (\,) and In(x)=In(left[x])⋅(x)⋅In(right[x])In(x) = In(left[x]) \cdot (x) \cdot In(right[x]). Because the subtrees T(left[x])T(left[x]) and T(right[x])T(right[x]) are disjoint and do not contain xx, In(x)In(x) lists each node of T(x)T(x) exactly once.

Consider the state of INORDER at each test of line L2 and at each test of line L3. Write the stack as s1,…,sks_1, \ldots, s_k from bottom to top, and define

pend=In(x)⋅(sk)⋅In(right[sk])⋅(sk−1)⋅In(right[sk−1])⋯(s1)⋅In(right[s1]).pend = In(x) \cdot (s_k) \cdot In(right[s_k]) \cdot (s_{k-1}) \cdot In(right[s_{k-1}]) \cdots (s_1) \cdot In(right[s_1]).

Lemma 1 (invariant). At every test of lines L2 and L3, out⋅pend=In(r)out \cdot pend = In(r).

Proof. By induction on the number of executed lines. Before the first test of line L2, line L1 has set out=( )out = (\,), SS empty and x=rx = r, so pend=In(r)pend = In(r).

Line L4 runs when x≠NILx \ne \mathrm{NIL}. Before it, pend=In(x)⋅Rpend = In(x) \cdot R for the rest RR. It pushes xx and sets x←left[x]x \gets left[x], so afterwards pend=In(left[x])⋅(x)⋅In(right[x])⋅Rpend = In(left[x]) \cdot (x) \cdot In(right[x]) \cdot R, which is In(x)⋅RIn(x) \cdot R by the definition of InIn. So pendpend and outout do not change.

Lines L5 to L7 run when the test of line L3 fails, so x=NILx = \mathrm{NIL} and In(x)=( )In(x) = (\,). The stack is not empty: the test of line L2 passed with x=NILx = \mathrm{NIL} or, if it passed with x≠NILx \ne \mathrm{NIL}, line L4 ran at least once. So pend=(sk)⋅In(right[sk])⋅R′pend = (s_k) \cdot In(right[s_k]) \cdot R', where R′R' is the part for s1,…,sk−1s_1, \ldots, s_{k-1}. Line L5 pops sks_k, line L6 appends it to outout, and line L7 sets x←right[sk]x \gets right[s_k]. Afterwards outout has gained sks_k at its end and pend=In(right[sk])⋅R′pend = In(right[s_k]) \cdot R', so out⋅pendout \cdot pend is unchanged. □\square

Lemma 2 (each node is pushed at most once). Every node of pendpend appears in it exactly once, and a node is pushed only when it is the root xx of the first part In(x)In(x) of pendpend.

Proof. out⋅pend=In(r)out \cdot pend = In(r) lists each node once, so each node is in exactly one of outout and pendpend. Line L4 pushes xx, which is in In(x)⊆pendIn(x) \subseteq pend. After the push, xx is a stack entry and stays in pendpend until line L5 pops it and line L6 moves it to outout. After that, it is in outout for good: no line removes entries from outout. A node in outout is not in pendpend, so it is never again the value of xx at line L4: the values of xx at line L4 are roots of subtrees whose nodes are all in pendpend. So each node is pushed at most once. □\square

Termination. Each pass of the inner loop pushes a node, so by Lemma 2 there are at most nn passes in all. Each pass of the outer loop pops a node, and a node can be popped only after it was pushed, so there are at most nn passes of the outer loop. So INORDER terminates, after at most nn pushes and nn pops.

Correctness. When the outer loop stops, x=NILx = \mathrm{NIL} and SS is empty, so pend=In(NIL)=( )pend = In(\mathrm{NIL}) = (\,), and by Lemma 1, out=In(r)out = In(r). Since In(r)In(r) has exactly nn entries, there were exactly nn pushes and nn pops, and line L8 returns the inorder list of the tree.

Search trees. If the tree is a binary search tree with distinct keys, then for every node xx the keys of T(left[x])T(left[x]) are smaller than key[x]key[x] and those of T(right[x])T(right[x]) are larger. By induction on the height, the keys of In(x)In(x) are increasing: the keys of In(left[x])In(left[x]) are increasing and below key[x]key[x], and those of In(right[x])In(right[x]) are increasing and above it. So the output lists the keys in sorted order. ■\blacksquare

Back to Inorder traversal