Algorithm Search⌘K

Proofs: Binary search tree

These are the human proofs of Binary search tree, at full length.

The operations of a binary search tree keep the invariant and return the right answers full

This proof shows: correctness.

We write S(T)S(T) for the set of keys of TT, and sub(x)\mathrm{sub}(x) for the set of nodes in the subtree rooted at xx. The invariant is (B1) order, (B2) links and (B3) count, as on the page. We use one consequence of (B1) many times.

Lemma 1 (intervals). Assume (B1). For each node xx, let a(x)a(x) be the largest key of an ancestor uu of xx such that xx is in the right subtree of uu (or −∞-\infty if there is none), and let b(x)b(x) be the smallest key of an ancestor uu such that xx is in the left subtree of uu (or +∞+\infty). Then each key in sub(x)\mathrm{sub}(x) lies in the open interval (a(x),b(x))(a(x), b(x)), and each key of TT in this interval is in sub(x)\mathrm{sub}(x).

Proof. The first part follows from (B1) at each ancestor uu. For the second part, let yy be a node with a(x)<y.key<b(x)a(x) < y.key < b(x), and let uu be the lowest common ancestor of xx and yy. If u≠xu \ne x, then xx and yy are in different subtrees of uu, or y=uy = u. If xx is in the left subtree of uu, then b(x)≤u.keyb(x) \le u.key, and y.key≥u.keyy.key \ge u.key by (B1) or by y=uy = u, so y.key≥b(x)y.key \ge b(x), which is false. The case where xx is in the right subtree is the mirror image. So u=xu = x, and y∈sub(x)y \in \mathrm{sub}(x). □\square

Search. Claim: TREE-SEARCH(T,k)(T, k) returns the node with key kk if k∈S(T)k \in S(T), and NIL else. We use the invariant of the loop of line L2: if k∈S(T)k \in S(T), then the node with key kk is in sub(x)\mathrm{sub}(x) (with sub(NIL)=∅\mathrm{sub}(\mathrm{NIL}) = \emptyset). Line L1 sets xx to the root, so it holds at the start. In a step, k≠x.keyk \ne x.key. If k<x.keyk < x.key, then by (B1) no node of the right subtree and not xx itself has key kk, so the node with key kk, if it exists, is in the left subtree, and line L3 moves there. The case k>x.keyk > x.key is the mirror image. Each step increases the depth of xx by 1, and the depth is at most hh, so the loop stops after at most h+1h + 1 steps. If it stops with x≠x \ne NIL, then x.key=kx.key = k. If it stops with $x = $ NIL, then sub(x)\mathrm{sub}(x) is empty, so by the invariant k∉S(T)k \notin S(T). The tree is not changed.

Minimum. Claim: TREE-MINIMUM(x)(x) returns the node of sub(x)\mathrm{sub}(x) with the smallest key. Let x0=x,x1,…,xmx_0 = x, x_1, \ldots, x_m be the path that lines L5 and L6 follow, where $x_m.left = $ NIL. For i<mi < m, each key of sub(xi)\mathrm{sub}(x_i) that is not in sub(xi+1)\mathrm{sub}(x_{i+1}) is xi.keyx_i.key or in the right subtree of xix_i, so it is larger than each key of sub(xi+1)\mathrm{sub}(x_{i+1}) by (B1). So the minimum of sub(xi)\mathrm{sub}(x_i) is the minimum of sub(xi+1)\mathrm{sub}(x_{i+1}). The node xmx_m has no left subtree, so by (B1) its key is the minimum of sub(xm)\mathrm{sub}(x_m). By induction from i=mi = m down to i=0i = 0, xmx_m is the minimum of sub(x)\mathrm{sub}(x).

Successor. Claim: TREE-SUCCESSOR(x)(x) returns the node with the smallest key larger than x.keyx.key, or NIL if x.keyx.key is the maximum. Case 1: x.right≠x.right \ne NIL. By Lemma 1 applied to r=x.rightr = x.right, the keys of TT in (x.key,b(x))(x.key, b(x)) are the keys of sub(r)\mathrm{sub}(r), because a(r)=x.keya(r) = x.key and b(r)=b(x)b(r) = b(x). This set is not empty, so the successor is its minimum, which line L7 returns by the claim on minimum. Case 2: $x.right = $ NIL. Then by Lemma 1 no key of TT lies in (x.key,b(x))(x.key, b(x)), so the successor is the node with key b(x)b(x) if b(x)<+∞b(x) < +\infty, and NIL else. The node with key b(x)b(x) is the lowest ancestor uu of xx such that xx is in the left subtree of uu: a lower such ancestor has a smaller key, because it is in the left subtree of each higher one. Lines L8 to L10 go up from xx while the current node is a right child, so they stop at the first ancestor yy entered from its left child, which is uu, or at NIL if there is none. By (B2) the parent pointers are right, so the walk follows the real path. The loop stops because the depth of xx drops by 1 in each step.

Insert. Claim: after TREE-INSERT(T,k,v)(T, k, v), the tree holds the map with kk set to vv, and (B1) to (B3) hold. If k∈S(T)k \in S(T), the loop of lines L12 to L15 is the search for kk, which reaches the node with key kk by the search claim, and line L15 changes only its value. Now let k∉S(T)k \notin S(T). The loop follows the search path for kk and stops when $x = $ NIL, with yy the last node of the path. Let cc be the empty child position of yy where the loop stopped (left if k<y.keyk < y.key, right else). For each node uu on the path above this position, the path went left at uu exactly when k<u.keyk < u.key. So the new node zz, placed at cc by lines L16 to L19, is in the left subtree of uu exactly when k<u.keyk < u.key, and (B1) holds at each ancestor of zz. Nodes that are not ancestors of zz keep their subtrees, so (B1) holds there too, and zz has no subtrees. Line L16 sets z.p=yz.p = y, and the link from yy (or from T.rootT.root when $y = $ NIL, which happens only for the empty tree) points to zz, so (B2) holds. The implementation adds 1 to nn in this case only, so (B3) holds.

Transplant. Claim: TRANSPLANT(T,u,v)(T, u, v) makes the parent of uu (or the root field) point to vv, and sets v.p=u.pv.p = u.p. This is lines L20 to L23 read directly; it changes no other link.

Delete. Claim: after TREE-DELETE(T,z)(T, z), S(T)S(T) loses exactly z.keyz.key, each other node keeps its key and value, and (B1) to (B3) hold. Let P=a(z)P = a(z) and Q=b(z)Q = b(z). By Lemma 1, each key of sub(z)\mathrm{sub}(z) lies in (P,Q)(P, Q). The new subtree that takes the place of zz has the keys of sub(z)\mathrm{sub}(z) without z.keyz.key, so (B1) at the ancestors of zz stays true, and we only need (B1) inside the new subtree.

  • Case A: $z.left = $ NIL (line L24). The new subtree is the right subtree of zz, which is a BST.
  • Case B: $z.right = $ NIL (line L25). The new subtree is the left subtree of zz, which is a BST.
  • Case C: two children (lines L26 to L28). Let LL and RR be the left and right subtrees of zz, and let $y = $ TREE-MINIMUM(z.right)(z.right), the node of RR with the smallest key, so $y.left = $ NIL. Case C1: y=z.righty = z.right. Line L28 puts yy in the place of zz, with LL as its left subtree; its right subtree stays R∖{y}R \setminus \{y\}. Each key of LL is less than z.key<y.keyz.key < y.key, and each key of R∖{y}R \setminus \{y\} is greater than y.keyy.key because yy is the minimum of RR. Case C2: y≠z.righty \ne z.right. Then yy is a left child inside RR. Line L27 replaces yy by its right subtree, which is (B1)-correct inside RR by the argument of case A applied to yy in the tree RR, so R′=R∖{y}R' = R \setminus \{y\} is a BST. Then yy gets R′R' as its right subtree, and line L28 puts yy in the place of zz with LL as its left subtree. Each key of LL is less than z.key<y.keyz.key < y.key, and each key of R′R' is greater than y.keyy.key. In both cases the new subtree is a BST with the keys of sub(z)\mathrm{sub}(z) except z.keyz.key.

For (B2): each link that the procedure changes is set in both directions. TRANSPLANT sets the child link and v.pv.p, and lines L27 and L28 set y.right.py.right.p and y.left.py.left.p after they set y.righty.right and y.lefty.left. The node zz is no longer reachable from the root, because its only parent link (or the root field) now points to its replacement. The implementation subtracts 1 from nn, so (B3) holds.

Termination of the loops. Each loop of the main block moves xx one level down (lines L2, L5 and L12) or one level up (line L9), so it runs at most h+1h + 1 times.

Theorem. Starting from the empty tree, after any sequence of inserts and deletes, the tree satisfies (B1) to (B3) and stores exactly the map that the sequence defines, and each search, minimum and successor query returns the answer of the ordered set.

Proof. By induction on the number of operations. The empty tree satisfies (B1) to (B3) and stores the empty map. By the insert and delete claims, each update keeps the invariant and changes the map as the dictionary laws require. Queries do not change the tree, and by the search, minimum and successor claims they return the right node. ■\blacksquare

Back to Binary search tree