Algorithm Search⌘K

Proofs: B+ tree

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

Search, range query, 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 order d≥1d \ge 1. The keys are distinct. An internal node xx has separators x.key[1]<⋯<x.key[m]x.key[1] < \cdots < x.key[m] and children x.c[0],…,x.c[m]x.c[0], \ldots, x.c[m]. We write x.key[0]=−∞x.key[0] = -\infty and x.key[m+1]=+∞x.key[m+1] = +\infty, and S(x)S(x) for the set of keys in the leaves below xx. A subtree is valid if:

(P1) The keys in each node are sorted. Each node has at most 2d2d keys, and an internal node with mm keys has m+1m + 1 children. (P2) For an internal node xx and 0≤i≤m0 \le i \le m, each key k∈S(x.c[i])k \in S(x.c[i]) has x.key[i]≤k<x.key[i+1]x.key[i] \le k < x.key[i+1]. (P3) All leaves have the same depth. (P4) Each node other than the root of the subtree has at least dd keys, and an internal root has at least 1 key. (P5) The leaves, from left to right, are linked by their next pointers, and the last leaf of the whole tree has next = NIL.

The keys of the tree are the keys in its leaves. Separators are only guides: a separator need not be a key of the tree. By (P2), the leaves from left to right hold the keys in increasing order.

Lemma 1 (the search path). In a valid tree, the leaf λ(k)\lambda(k) that lines L1 to L5 return is the only leaf that can hold kk, and every key k′k' of a leaf to the left of λ(k)\lambda(k) has k′<kk' < k.

Proof. At an internal node xx, line L3 takes ii = the number of separators ≤k\le k, so x.key[i]≤k<x.key[i+1]x.key[i] \le k < x.key[i+1]. By (P2), each key of a child x.c[j]x.c[j] with j<ij < i is <x.key[j+1]≤x.key[i]≤k< x.key[j+1] \le x.key[i] \le k, and each key of a child with j>ij > i is ≥x.key[j]≥x.key[i+1]>k\ge x.key[j] \ge x.key[i+1] > k. So kk can only be in S(x.c[i])S(x.c[i]), and every leaf to the left of x.c[i]x.c[i] below xx holds only keys smaller than kk. By induction down the path, the same holds for the leaf that the loop reaches. The loop stops, because each step goes one level down and the leaves are at a finite depth (P3). □\square

Theorem 1 (search and range). In a valid tree, the search for kk is true exactly when kk is a key, and RANGE(T,a,b)(T, a, b) returns the keys kk with a≤k≤ba \le k \le b in increasing order.

Proof. The first claim follows from Lemma 1. For the range, line L6 starts at λ(a)\lambda(a). By Lemma 1, all keys in leaves to its left are smaller than aa, so no answer is missed there. Lines L7 to L10 visit the leaves from λ(a)\lambda(a) to the right through the next links, which by (P5) is the order of the keys. Line L8 skips keys below aa and stops at the first key above bb, and every later key is larger still. So the output is exactly the keys in [a,b][a, b], in order. □\square

Lemma 2 (insert below a node). Let the subtree of xx be valid, and let k∉S(x)k \notin S(x). Then INSERT-REC(x,k)(x, k) adds kk to the keys below xx, keeps (P1) to (P3) and (P5) and keeps (P4) below xx, and either

  • returns NIL, and xx has at most 2d2d keys, or
  • returns (s,z)(s, z), where zz is a new node at the depth of xx, placed right after xx in the leaf order, xx and zz each have at least dd keys and at most 2d2d, every key below xx is <s< s, and every key below zz is ≥s\ge s.

Proof. By induction on the height of xx. If xx is a leaf, line L15 adds kk in order. If it then has at most 2d2d keys, line L16 returns NIL. Otherwise it has 2d+12d + 1 keys. Line L17 keeps the dd smallest in xx and puts the other d+1d + 1 in zz, line L18 links zz between xx and the old successor of xx, so (P5) holds, and line L19 returns the smallest key of zz as ss. Every key of xx is smaller than ss.

If xx is internal, line L20 picks the child x.c[i]x.c[i] whose range contains kk, as in Lemma 1, and line L21 applies the hypothesis to it. If it returns NIL, (P2) at xx holds because kk went into the right range. Otherwise line L23 inserts ss after x.key[i]x.key[i] and zz after x.c[i]x.c[i]. The keys below the old x.c[i]x.c[i] were in [x.key[i],x.key[i+1])[x.key[i], x.key[i+1]), they are now split into those below x.c[i]x.c[i], all <s< s, and those below zz, all ≥s\ge s. So (P2) holds at xx for the two new ranges [x.key[i],s)[x.key[i], s) and [s,x.key[i+1])[s, x.key[i+1]), and the separators stay sorted. If xx now has at most 2d2d keys, line L24 returns NIL. Otherwise it has 2d+12d + 1 keys and 2d+22d + 2 children. Lines L25 and L26 keep the first dd keys and the first d+1d + 1 children in xx, move the last dd keys and the last d+1d + 1 children to z′z', and return the middle key s′=x.key[d+1]s' = x.key[d+1]. By (P2) before the split, each key below the first d+1d + 1 children is <s′< s' and each key below the others is ≥s′\ge s'. Both halves have dd keys, and (P2) holds inside each half because each child keeps its two neighbouring separators, or −∞-\infty and +∞+\infty replaced by the bounds that the parent gives. The depth of each leaf does not change. □\square

Theorem 2 (insert). If TT is a valid B+ tree and kk is not a key, then after BPT-INSERT(T,k)(T, k) the tree is a valid B+ tree whose keys are the old keys and kk.

Proof. Lemma 2 applies to the root. If it returns NIL, nothing more is needed. If it returns (s,z)(s, z), line L13 makes a new root with the one separator ss and the children old root and zz. (P2) holds at the new root by Lemma 2, the new root has one key, so (P4) holds, and all leaves are now one level deeper, so (P3) holds. □\square

Lemma 3 (fix a child). Let xx be internal, let the subtrees of its children be valid, let (P2) hold at xx, and let c=x.c[i]c = x.c[i] have exactly d−1d - 1 keys while each other child has at least dd. Then FIX-CHILD(x,i)(x, i) keeps the keys below xx, keeps (P2), (P3) and (P5), leaves each child of xx with dd to 2d2d keys, and removes at most one separator from xx.

Proof. A sibling yy next to cc exists, because xx has at least 2 children (xx has at least 1 key by (P1) and (P4) or by the root rule).

Borrow from a leaf (line L36, yy has more than dd keys). Say y=x.c[i−1]y = x.c[i-1] is the left sibling. Its largest key moves to the front of cc, and the separator x.key[i]x.key[i] becomes the new smallest key of cc. The keys left in yy are below the moved key, and those in cc are at least it, so (P2) holds. The moved key was ≥x.key[i−1]\ge x.key[i-1], so the range of yy stays correct. Now cc has dd keys and yy at least dd. The right sibling is the mirror image, with the new separator equal to the new smallest key of yy.

Borrow from an internal node. With the left sibling yy: the separator x.key[i]x.key[i] moves down to the front of cc, the last child of yy becomes the first child of cc, and the last key of yy moves up to become x.key[i]x.key[i]. This is a rotation. The keys below the moved child lie between the last key of yy and the old x.key[i]x.key[i], which are the new bounds of that child inside cc. So (P2) holds at xx, at yy and at cc, and no leaf changes depth. The right sibling is the mirror image.

Merge (line L37, yy has exactly dd keys). Let aa be the left one of cc and yy, and bb the right one, with separator ss between them in xx. If they are leaves, aa takes all keys of bb and the link b.nextb.next. Together they have 2d−1≤2d2d - 1 \le 2d keys, in order, because the keys of aa are <s≤< s \le the keys of bb. If they are internal, aa takes ss, then the keys and children of bb, for (d−1)+1+d=2d(d - 1) + 1 + d = 2d keys. The children of bb had bounds from bb's separators and from ss and the next separator of xx, and these are now the separators around them in aa. Then xx loses ss and the child bb, and the range of aa in xx is the union of the two old ranges. Leaves of aa and bb had the same depth by (P3). □\square

Lemma 4 (delete below a node). Let the subtree of xx be valid. Then DELETE-REC(x,k)(x, k) removes kk from the keys below xx if it is there, keeps (P1) to (P3), (P5), and (P4) at each node below xx other than xx, removes at most one key from xx, and returns true exactly when xx ends with fewer than dd keys.

Proof. By induction on the height. A leaf removes kk if it holds it (line L30). An internal node goes to the only child whose range can hold kk (line L31), by Lemma 1. By the hypothesis that child keeps all properties below it and loses at most one key, so it has at least d−1d - 1 keys. If it has fewer than dd, line L33 applies Lemma 3, which removes at most one separator from xx. Line L34 reports whether xx dropped below dd. □\square

Theorem 3 (delete). If TT is a valid B+ tree, after BPT-DELETE(T,k)(T, k) it is a valid B+ tree on its old keys without kk.

Proof. Lemma 4 applies to the root, and the root may have any number of keys, so (P4) holds except that an internal root may have no key. Then it has exactly one child, and line L29 makes that child the root. All leaves move up one level, so (P3) holds. □\square

Termination. Each recursive call of INSERT-REC and DELETE-REC goes one level down, and the search loop of lines L2 to L4 does too, so there are at most h+1h + 1 calls, where hh is the height. The range loop visits each leaf at most once, because the next links go strictly to the right. ■\blacksquare

Back to B+ tree