Algorithm Search⌘K

Proofs: 2-3 tree

These are the human proofs of 2-3 tree, at full length.

Insertion and deletion keep the 2-3 tree invariant full

This proof shows: correctness.

Write K(t)K(t) for the set of keys in the subtree tt and d(t)d(t) for the number of nodes on a path from tt down to a leaf. A subtree is valid when it satisfies (T1) each node has one or two keys in increasing order, (T2) an internal node with jj keys has j+1j + 1 children, and for keys x1<⋯<xjx_1 < \dots < x_j the child ii (counted from 0) holds only keys between xix_i and xi+1x_{i+1}, with x0=−∞x_0 = -\infty and xj+1=+∞x_{j+1} = +\infty, and (T3) all paths from the root of the subtree to a leaf have the same number of nodes. In a valid subtree, d(t)d(t) is well defined.

Insertion

Lemma 1 (INS). Let tt be a valid subtree and kk a key. After INS(t,k)(t, k):

  1. if k∈K(t)k \in K(t), the result is nil and tt is unchanged;
  2. else, if the result is nil, tt is valid with K(t)∪{k}K(t) \cup \{k\} and the same d(t)d(t);
  3. else the result is (m,a,b)(m, a, b), where aa and bb are valid with d(a)=d(b)=d(t)d(a) = d(b) = d(t), each key of aa is less than mm, each key of bb is greater than mm, and K(a)∪{m}∪K(b)=K(t)∪{k}K(a) \cup \{m\} \cup K(b) = K(t) \cup \{k\}.

Proof. By induction on d(t)d(t). If kk is a key of tt, line L4 returns nil. Else, if tt is a leaf, line L5 puts kk into the sorted list of tt; if kk is in a deeper node, then tt is internal and the case is handled below.

Leaf. After line L5, tt has two or three keys in order. With two keys, line L13 returns nil, and tt is a valid leaf with d(t)=1d(t) = 1. With three keys x<y<zx < y < z, line L12 returns (y,[x],[z])(y, [x], [z]), two leaves, which satisfy part 3.

Internal node. Line L7 picks the child c=t.kids[i]c = t.kids[i] whose range holds kk; by (T2), if kk is in the tree below tt, it is in cc. By the induction hypothesis on cc, which has d(c)=d(t)−1d(c) = d(t) - 1: if k∈K(c)k \in K(c), the result is nil and nothing changed. If line L8 returns nil with k∉K(c)k \notin K(c), then cc is valid with the new key and the same depth, and kk lies in the range of child ii, so tt is valid; line L9 returns nil. Else line L8 returns (m,a,b)(m, a, b). Line L10 puts mm at position ii of t.keyst.keys and replaces cc by aa and bb. Since mm and the keys of aa and bb come from K(c)∪{k}K(c) \cup \{k\}, they lie in the range of cc, between xix_i and xi+1x_{i+1}; so the keys of tt stay in order, and aa, bb lie in the ranges that mm cuts out of the old range. The number of children is one more than before, as is the number of keys, and d(a)=d(b)=d(c)d(a) = d(b) = d(c), so (T2) and (T3) hold. If tt now has two keys, line L13 returns nil. If it has three keys x<y<zx < y < z and four children c0,c1,c2,c3c_0, c_1, c_2, c_3, line L12 returns yy, the node [x][x] with c0,c1c_0, c_1, and the node [z][z] with c2,c3c_2, c_3. By (T2) for the 4-node, the keys of c0c_0 and c1c_1 are less than yy and those of c2c_2, c3c_3 are greater, and the two new nodes satisfy (T1) to (T3) with depth d(t)d(t). □\square

INSERT. If the tree is empty, line L1 makes a valid leaf. Else, by Lemma 1, either the root is valid with the new key, or line L3 makes a new root [m][m] with the children aa and bb. This root is valid: aa and bb have the same depth, and their keys lie on the right sides of mm. All leaves move one level down together.

Deletion

An empty node is a node with no keys and one child (internal) or none (leaf). A subtree is nearly valid when it is valid, or its root is empty and its only child is valid (or it is a single empty leaf). Define dd of an empty node as 1 plus dd of its child, or 1 for an empty leaf.

Lemma 2 (FIX). Let tt be a node whose children are valid, except that c=t.kids[i]c = t.kids[i] is nearly valid with an empty root, and all children have the same dd. Let the keys of tt and its children satisfy (T2). After FIX(t,i)(t, i), all children of tt are valid with the same dd as before, the keys in the subtree of tt are the same, the keys of tt and its children satisfy (T2), and tt has one key less than before or the same number.

Proof. Assume the sibling ss is t.kids[i+1]t.kids[i + 1] and x=t.keys[i]x = t.keys[i] is the key between them; the case s=t.kids[i−1]s = t.kids[i - 1] is the mirror image. Every key in cc is less than xx and every key in ss is greater.

Borrow (lines L22 and L23). Let ss have keys y<zy < z and, if it is internal, children s0,s1,s2s_0, s_1, s_2. Then cc gets the key xx and, after its own child c0c_0, the child s0s_0; t.keys[i]t.keys[i] becomes yy; and ss keeps zz with s1,s2s_1, s_2. The keys of c0c_0 are less than xx (they were in cc), and the keys of s0s_0 are between xx and yy (they were in ss, below yy). So cc is a valid 2-node, and it lies below the new separator yy. The node ss is a valid 2-node above yy. Since c0c_0 and s0s_0 had the same depth, d(c)=d(s)d(c) = d(s) is as before. tt has the same number of keys.

Merge (lines L24 and L25). Let ss have one key yy and, if internal, children s0,s1s_0, s_1. The merged node has keys x<yx < y and children c0,s0,s1c_0, s_0, s_1, with the keys of c0c_0 below xx, those of s0s_0 between xx and yy, and those of s1s_1 above yy. It is a valid 3-node with the old depth. The key xx and the child cc leave tt, which keeps one child fewer and one key fewer, so the key ranges of tt are still right. □\square

Lemma 3 (DEL). Let tt be valid. After DEL(t,k)(t, k), the subtree of tt is nearly valid, holds K(t)∖{k}K(t) \setminus \{k\}, and has the same dd as before.

Proof. By induction on d(t)d(t). Leaf (line L17). Removing kk leaves zero or one key, or two when kk was absent; the result is a leaf, so it is nearly valid with d=1d = 1.

Internal node. If kk is a key xjx_j of tt, line L19 finds the least key ss of the child j+1j + 1 by going to the first child until a leaf. By (T2), xj<sx_j < s and every other key of that subtree is greater than ss. So after t.keys[j]←st.keys[j] \leftarrow s, the order (T2) holds at tt for the tree in which the child j+1j + 1 still holds ss, and it is enough to delete ss from it, which line L20 does with i=j+1i = j + 1. If kk is not a key of tt, line L18 picks the child ii whose range holds kk, and kk can only be there. By the induction hypothesis, after line L20 the child ii is nearly valid with the right keys and the same depth. If it is not empty, tt is valid. If it is empty, line L21 calls FIX, and by Lemma 2 all children of tt are valid with the old depth. Then tt has at least zero keys and at most its old number, and its children satisfy (T2) and (T3), so the subtree of tt is nearly valid: either tt is valid, or tt lost its only key in a merge and has one child, which is valid. □\square

DELETE. By Lemma 3, after line L15 the root is valid or empty. If it is empty, line L16 replaces it by its only child, which is valid, or by nil. All leaves move one level up together, so (T3) holds.

Conclusion

Theorem. Starting from the empty tree, after any sequence of insertions and deletions the tree is a valid 2-3 tree that holds exactly the keys inserted and not deleted since, and a search returns true exactly for these keys.

Proof. The empty tree is valid. INSERT and DELETE keep the tree valid and change the key set as stated, by Lemma 1 and Lemma 3. A search at a node either finds kk among its keys, or goes to the child whose range holds kk; by (T2), kk can be in the subtree only there. It reaches a leaf after d(root)d(\text{root}) steps. ■\blacksquare

Back to 2-3 tree