Algorithm Search⌘K

Proofs: B-tree

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

Search, insert and delete keep the B-tree properties and give the right answers full

This proof shows: correctness, termination.

We use the pseudocode of the page and the minimum degree t≥2t \ge 2. The keys are distinct. For a node xx, S(x)S(x) is the set of keys in the subtree of xx, and ht(x)\mathrm{ht}(x) is the number of edges from xx down to a leaf of its subtree. A subtree is valid if it satisfies these properties at each of its nodes yy:

(B1) y.key[1]<⋯<y.key[y.n]y.key[1] < \cdots < y.key[y.n] and y.n≤2t−1y.n \le 2t - 1. If yy is internal, it has y.n+1y.n + 1 children. (B2) If yy is internal, each key of S(y.c[i])S(y.c[i]) is larger than y.key[i−1]y.key[i-1] (when i≥2i \ge 2) and smaller than y.key[i]y.key[i] (when i≤y.ni \le y.n). (B3) All leaves of the subtree of yy have the same depth. (B4) y.n≥t−1y.n \ge t - 1, except at the root of the subtree, where y.n≥1y.n \ge 1 if yy is internal.

A tree is a B-tree when the subtree of its root is valid. By (B2), an in-order walk lists S(y)S(y) in increasing order, and ht(y.c[i])=ht(y)−1\mathrm{ht}(y.c[i]) = \mathrm{ht}(y) - 1 for each child, by (B3).

Lemma 1 (search). If the subtree of xx is valid, B-TREE-SEARCH(x,k)(x, k) returns (x′,i)(x', i) with x′.key[i]=kx'.key[i] = k when k∈S(x)k \in S(x), and NIL otherwise. It makes at most ht(x)\mathrm{ht}(x) disk reads.

Proof. By induction on ht(x)\mathrm{ht}(x). Lines L1 and L2 stop at the least ii with i>x.ni > x.n or k≤x.key[i]k \le x.key[i]. So x.key[i−1]<kx.key[i-1] < k when i≥2i \ge 2, and k≤x.key[i]k \le x.key[i] when i≤x.ni \le x.n. If line L3 finds k=x.key[i]k = x.key[i], the answer is right. Otherwise kk is not a key of xx, because the keys of xx before position ii are smaller than kk and those from position ii on are larger. If xx is a leaf, S(x)S(x) is the keys of xx, and line L4 returns NIL correctly. If xx is internal, by (B2) every key of S(x)S(x) that lies strictly between x.key[i−1]x.key[i-1] and x.key[i]x.key[i] is in S(x.c[i])S(x.c[i]), and kk lies there, so k∈S(x)k \in S(x) if and only if k∈S(x.c[i])k \in S(x.c[i]). The child has height ht(x)−1\mathrm{ht}(x) - 1, and its subtree is valid (with the weaker root rule of (B4), which the search does not use). The induction hypothesis gives the answer, with at most ht(x)−1\mathrm{ht}(x) - 1 reads, plus the one read of line L5. In the base case ht(x)=0\mathrm{ht}(x) = 0, xx is a leaf and no read happens. □\square

Lemma 2 (split). Let xx be an internal node that is not full, and let y=x.c[i]y = x.c[i] be full, with 2t−12t - 1 keys. After SPLIT-CHILD(x,i)(x, i), the subtree of xx has the same key set and the same leaf depths, yy and the new node zz have t−1t - 1 keys each, xx has one key more, and (B1) to (B3) still hold at each node.

Proof. Write a1<⋯<a2t−1a_1 < \cdots < a_{2t-1} for the keys of yy and T1,…,T2tT_1, \ldots, T_{2t} for its child subtrees, if any. Lines L14 to L17 leave a1,…,at−1a_1, \ldots, a_{t-1} and T1,…,TtT_1, \ldots, T_t in yy, and put at+1,…,a2t−1a_{t+1}, \ldots, a_{2t-1} and Tt+1,…,T2tT_{t+1}, \ldots, T_{2t} in zz. Lines L18 and L19 put ata_t into xx at position ii, with yy to its left and zz to its right. No key is lost: ata_t moved up, and the others stay below. (B1) holds at yy and zz, which have t−1t - 1 keys and tt children each if internal. At xx, x.nx.n grows by one, to at most 2t−12t - 1 because xx was not full, and the keys stay sorted: by (B2) at xx before the split, ata_t lies between x.key[i−1]x.key[i-1] and the old x.key[i]x.key[i]. (B2) holds at yy and zz because their children kept their old neighbouring keys, and at xx because the keys of yy are below ata_t and those of zz are above it. The subtrees TjT_j keep their depth below xx, so (B3) holds. □\square

Lemma 3 (insert into a node that is not full). Let the subtree of xx be valid, let xx be not full, and let k∉S(x)k \notin S(x). Then INSERT-NONFULL(x,k)(x, k) makes the subtree of xx hold S(x)∪{k}S(x) \cup \{k\}, keeps it valid, keeps the depth of its leaves, and does not touch a node outside it.

Proof. By induction on ht(x)\mathrm{ht}(x). If xx is a leaf, lines L23 and L24 shift the keys larger than kk one place right and put kk in the gap. The keys stay sorted, and x.n≤2t−1x.n \le 2t - 1 because xx was not full. If xx is internal, line L25 finds the child x.c[i]x.c[i] whose key range, by (B2), contains kk. If that child is full, line L28 splits it, which is allowed because xx is not full, and Lemma 2 keeps the subtree valid. After the split, kk is in the range of x.c[i]x.c[i] or of x.c[i+1]x.c[i+1], and line L29 picks the right one by comparing kk with the new key x.key[i]x.key[i], which is not kk because k∉S(x)k \notin S(x). In both cases the chosen child is not full: it was not full, or it is one of the two halves with t−1<2t−1t - 1 < 2t - 1 keys. Its subtree is valid and has height ht(x)−1\mathrm{ht}(x) - 1, so the induction hypothesis applies to line L30. The child keeps at least t−1t - 1 keys and gains at most one, so (B4) holds there, and (B2) holds at xx because kk went into the right range. □\square

Theorem 1 (insert). If TT is a B-tree and kk is not in TT, then after B-TREE-INSERT(T,k)(T, k) the tree TT is a B-tree with the keys of TT and kk.

Proof. If the root rr is not full, line L13 applies Lemma 3 to rr. If rr is full, lines L9 and L10 make a new root ss with no key and the single child rr, and line L11 splits rr. Lemma 2 applies to ss, which is not full, and leaves ss with one key and two children with t−1t - 1 keys each, so (B4) holds. All leaves are now one level deeper, all by the same amount, so (B3) holds. Then line L13 applies Lemma 3 to ss. □\square

Lemma 4 (delete). Let the subtree of xx be valid, and let xx be the root of the tree or have at least tt keys. Then B-TREE-DELETE(x,k)(x, k) makes the subtree of xx hold S(x)∖{k}S(x) \setminus \{k\}, keeps (B1) to (B3) at each of its nodes and (B4) at each of its nodes other than xx, removes at most one key from xx, keeps the depth of its leaves, and does not touch a node outside it.

Proof. By induction on ht(x)\mathrm{ht}(x). Line L31 finds the least ii with k≤x.key[i]k \le x.key[i], or i=x.n+1i = x.n + 1.

Case 1, line L33. kk is a key of the leaf xx. Removing it keeps the keys sorted and removes one key.

Case 2a, line L35. k=x.key[i]k = x.key[i], xx is internal and y=x.c[i]y = x.c[i] has at least tt keys. Let k′k' be the largest key of S(y)S(y). By (B2), k′k' is the largest key of S(x)S(x) below kk. The deletion of k′k' from yy touches only the subtree of yy, so it gives the same tree whether x.key[i]←k′x.key[i] \leftarrow k' is done before it or after it. Do it before it in the analysis order "delete, then replace". The node yy has at least tt keys, so the induction hypothesis applies: the subtree of yy becomes valid on S(y)∖{k′}S(y) \setminus \{k'\}, with at least t−1t - 1 keys at yy. All these keys are below k′k', and k′<kk' < k is above every key of x.c[i−1]x.c[i-1]. So writing k′k' in place of kk keeps (B1) and (B2) at xx. The number of keys of xx does not change.

Case 2b, line L36. The same with the smallest key of S(z)S(z), z=x.c[i+1]z = x.c[i+1], which has at least tt keys.

Case 2c, line L37. Both yy and zz have t−1t - 1 keys. The new yy is yy, then kk, then zz, with 2t−12t - 1 keys and, if internal, 2t2t children. Its keys are sorted by (B2) at xx, so (B1) holds. xx loses the key kk and the child zz. The children of yy and zz have the same depth by (B3), so (B3) holds. Now yy has 2t−1≥t2t - 1 \ge t keys and k∈S(y)k \in S(y), and the induction hypothesis deletes kk from the subtree of yy, leaving at least 2t−2≥t−12t - 2 \ge t - 1 keys at yy.

Line L38. kk is not a key of xx and xx is a leaf, so k∉S(x)k \notin S(x), and nothing changes.

Case 3, lines L39 to L41. kk is not a key of xx, xx is internal, and by (B2) k∈S(x)k \in S(x) only if k∈S(x.c[i])k \in S(x.c[i]). If c=x.c[i]c = x.c[i] has at least tt keys, the induction hypothesis applies to it directly. Else cc has t−1t - 1 keys. In case 3a a sibling, say the left one ℓ=x.c[i−1]\ell = x.c[i-1], has at least tt keys. The key x.key[i−1]x.key[i-1] moves down to the front of cc, the last key of ℓ\ell moves up in its place, and the last child of ℓ\ell becomes the first child of cc. The in-order sequence of the subtree of xx is the same as before, so (B1) and (B2) hold, no depth changes, ℓ\ell keeps at least t−1t - 1 keys, and cc now has tt. The right sibling is the mirror image. In case 3b both siblings that exist have t−1t - 1 keys, and cc is merged with one of them and the key of xx between them, as in case 2c. The merged node has 2t−12t - 1 keys, xx loses one key, and (B1) to (B3) hold as in case 2c. In both cases, line L42 continues at a child with at least tt keys, whose key range contains kk, and the induction hypothesis applies.

In each case xx loses at most one key, so it keeps at least t−1t - 1 keys if it had at least tt. Each child that the procedure leaves behind kept or gained keys, or is the node of the recursive call, which keeps (B4) by the hypothesis. A node outside the subtree of xx is never touched, because each step changes only xx and its children. In the base case ht(x)=0\mathrm{ht}(x) = 0, xx is a leaf and only case 1 or line L38 applies. □\square

Theorem 2 (delete). If TT is a B-tree, then after B-TREE-DELETE(T.root,k)(T.root, k) and the final check, TT is a B-tree on its old keys without kk.

Proof. Lemma 4 applies to the root, whatever its number of keys. Afterwards (B1) to (B4) hold at each node except that the root may have 0 keys. If the root is a leaf with 0 keys, the tree is empty. If it is internal with 0 keys, it has exactly one child by (B1), and the final check makes that child the new root. All leaves become one level less deep, all by the same amount, so (B3) holds, and the new root has at least t−1≥1t - 1 \ge 1 keys. □\square

Termination. Each recursive call, or each round of the loop in the code, moves from a node xx to one of its children, or to a node in the subtree of a child, so ht\mathrm{ht} of the current node drops by at least 1. The search for the largest key of yy in case 2a also moves down one level at each step. So each operation stops after at most h+1h + 1 rounds of each kind, where hh is the height of the tree. ■\blacksquare

Back to B-tree