Proofs: Ternary search tree
These are the human proofs of Ternary search tree, at full length.
The ternary search tree operations keep the invariant and answer the string symbol table queries full
This proof shows: correctness.
We use the pseudocode, the sibling trees, the strings and the invariant (S1) to (S4) of the page. The level of a sibling tree is the length of the strings of its nodes: the sibling tree of the root has level 1, and the one below has level . All nodes of one sibling tree share the same except for the last symbol.
Lemma 1 (uniqueness). Under (S1), each nonempty string is for at most one node .
Proof. By induction on . A node with lies in a sibling tree of level and has . For that sibling tree is the one of the root. For it is the sibling tree below for a node with , and is unique by the induction hypothesis. In either case the sibling tree is fixed, and by (S1) its symbols are distinct, so at most one of its nodes has the symbol .
Lemma 2 (walk). The loop of lines L1 to L7 returns for the node with if this node exists, and NIL otherwise.
Proof. The loop keeps this claim: is NIL or a node of the sibling tree of level whose nodes have strings , and if a node with exists, then is in the subtree of within that sibling tree. At the start, is the root and , so the claim holds. Lines L3 and L4 are one step of a BST search for the symbol : by (S1) a node with symbol smaller than can only be in the left subtree, and one with a larger symbol only in the right subtree. If , then . If , then is the node of , and line L6 returns its value. Else line L5 moves to the sibling tree below , which by definition holds exactly the nodes with strings , and the claim holds for . If becomes NIL, the subtree where must be is empty, so the node of does not exist, and then no node of exists either (its sibling tree would be below ). Line L7 returns NIL. Each round either moves one level down in a sibling tree or increases ; a sibling tree is finite and , so the loop ends.
GET. By Lemma 2 and (S2), GET returns the value of if is a key and NIL otherwise.
PUT. The loop of lines L9 to L14 takes the same steps as the walk of Lemma 2, but where the walk would reach a NIL link, it creates a node there. A node created by a left or right step at level gets the symbol , and it is put at the place where the BST search for ended, so it is larger than each node where the search went right and smaller than each node where it went left. This is a BST insert, and (S1) holds in that sibling tree. A node created by a middle step starts a new sibling tree with one node, where (S1) holds. When the loop reaches line L13, is the node with . Lines L13 and L14 give it the value and count it if it was new as a key, so (S2) and (S4) hold. For (S3), each node created in this call lies on the path from the root to , and each one other than was created just before a step into it, so the nodes created by middle steps have a middle child or are ; a node created by a left or right step at level has the symbol , so the next step from it is a middle step (or it is ), which gives it a middle child. The value of covers .
DELETE. If line L16 returns, is not a key by Lemma 2 and (S2), and nothing changes. Else line L17 removes the value of the node of , so (S2) and (S4) hold for the keys other than . Now (S3) can fail only at nodes of the path that lost their value or their middle child. The loop keeps this claim: (S1), (S2) and (S4) hold, and (S3) holds at each node except possibly the current node . At , if has a value or a middle child, (S3) holds everywhere, and the loop returns. Else no node lies below and has no value, so carries no key and no other node's string depends on it. Line L20 removes from its sibling tree with a BST delete, which keeps the BST order of the other nodes of that sibling tree (this is the correctness of the BST delete) and does not change their strings, since the sibling tree and the symbols of its nodes stay the same. So (S1), (S2) and (S4) still hold. The only node whose links changed in a way that can break (S3) is the parent of the sibling tree, when was the root of the sibling tree below and the sibling tree is now empty. Then is the next node of the path. If was reached by a left or right link, its parent in the path keeps its middle child and its value, so the next round returns. So the claim holds for the next node of the path. The loop ends at the root of the tree at the latest.
KEYS-WITH-PREFIX. If , the walk starts at the root with prefix . Else, by Lemma 2, line L21 finds the node with or returns the empty list. In the second case no key starts with : the node of a key with has the node of as an ancestor through middle links, by the construction of . In the first case, is reported if it is a key, and the other keys with prefix are the strings of the nodes with a value in the tree below , by (S2) and the definition of .
The walk of lines L22 to L24 visits each node of the given tree once, with the prefix such that : this holds at the start, a left or right step keeps (same sibling tree), and a middle step extends it by (line L24). So line L23 appends exactly the keys of the tree. Order. Within a sibling tree the in-order visit takes the symbols in increasing order, by (S1). At , the string is reported before the keys below , which extend it, and all of these come after the keys of the left subtree of and before those of the right subtree, whose last symbol at this level is smaller or larger. These are the rules of lexicographic order, so the list is sorted.