Algorithm Search⌘K

Proofs: Binary search symbol table

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

RANK counts the smaller keys, and PUT and DELETE keep the arrays sorted full

This proof shows: correctness.

Invariant (I). keys[0]<⋯<keys[n−1]keys[0] < \cdots < keys[n-1], and the table holds exactly the pairs (keys[i],vals[i])(keys[i], vals[i]) for 0≤i<n0 \le i < n.

For a key kk, let r(k)=∣{i<n:keys[i]<k}∣r(k) = \lvert \{ i < n : keys[i] < k \} \rvert. Under (I), the keys less than kk are exactly keys[0..r(k)−1]keys[0..r(k) - 1], and if kk is in the table, it is keys[r(k)]keys[r(k)].

Lemma (RANK). Under (I), RANK(T,k)(T, k) returns r(k)r(k) after at most ⌊lg⁡n⌋+1\lfloor \lg n \rfloor + 1 passes of the loop.

Proof. We use the loop invariant

(R) 0≤lo≤hi+1≤n0 \le lo \le hi + 1 \le n, every key in keys[0..lo−1]keys[0..lo-1] is less than kk, and every key in keys[hi+1..n−1]keys[hi+1..n-1] is greater than kk.

Line L1 sets lo=0lo = 0 and hi=n−1hi = n - 1; both ranges are empty and (R) holds. In a pass, lo≤hilo \le hi, and line L3 gives lo≤mid≤hilo \le mid \le hi. If k<keys[mid]k < keys[mid], then since the keys increase, every key in keys[mid..n−1]keys[mid..n-1] is greater than kk, and line L4 sets hi=mid−1≥lo−1hi = mid - 1 \ge lo - 1; (R) holds. If k>keys[mid]k > keys[mid], every key in keys[0..mid]keys[0..mid] is less than kk, and line L5 sets lo=mid+1≤hi+1lo = mid + 1 \le hi + 1; (R) holds. Otherwise keys[mid]=kkeys[mid] = k, and line L6 returns midmid; since the keys increase strictly, exactly the midmid keys before it are less than kk, so mid=r(k)mid = r(k).

If the loop ends, lo>hilo > hi, and with (R), lo=hi+1lo = hi + 1. Then the two ranges of (R) cover all nn indices, so exactly lolo keys are less than kk, and line L7 returns lo=r(k)lo = r(k).

Passes. Let s=hi−lo+1s = hi - lo + 1 be the size of the range. Line L3 splits it into mid−lo=⌊(s−1)/2⌋mid - lo = \lfloor (s - 1)/2 \rfloor cells to the left of midmid and s−1−⌊(s−1)/2⌋=⌈(s−1)/2⌉s - 1 - \lfloor (s - 1)/2 \rfloor = \lceil (s - 1)/2 \rceil to the right. Either way the new size is at most ⌊s/2⌋\lfloor s / 2 \rfloor. Starting from s=ns = n, after tt passes s≤⌊n/2t⌋s \le \lfloor n / 2^t \rfloor, which is 0 for t=⌊lg⁡n⌋+1t = \lfloor \lg n \rfloor + 1. So there are at most ⌊lg⁡n⌋+1\lfloor \lg n \rfloor + 1 passes. □\square

GET(T,k)(T, k). By the lemma, line L8 finds i=r(k)i = r(k). If kk is in the table, it is keys[i]keys[i], and line L8 returns its value vals[i]vals[i] by (I). If it is not, then either i=ni = n or keys[i]≠kkeys[i] \ne k, and line L8 returns NIL. Nothing is written.

PUT(T,k,v)(T, k, v). Let i=r(k)i = r(k) from line L9. If i<ni < n and keys[i]=kkeys[i] = k, line L10 replaces the value of kk and changes no key, so (I) holds for the table with (k,v)(k, v) in place of the old pair of kk. Otherwise kk is not in the table, and keys[0..i−1]<k<keys[i..n−1]keys[0..i-1] < k < keys[i..n-1]. The loop of line L11 runs j=n,n−1,…,i+1j = n, n - 1, \ldots, i + 1. At step jj it copies cell j−1j - 1 to cell jj. Cell jj is either the free cell nn (for j=nj = n) or a cell already copied at step j+1j + 1; cell j−1j - 1 is not yet written. So after the loop, $keys[j] $ holds the old keys[j−1]keys[j - 1] for i<j≤ni < j \le n, and cells 0..i−10..i - 1 are unchanged; the same holds for valsvals. Line L12 writes (k,v)(k, v) at index ii and sets the count to n+1n + 1. The new key sequence is $keys[0..i-1], k, $ old keys[i..n−1]keys[i..n-1], which is strictly increasing, and the pairs are the old pairs and (k,v)(k, v). So (I) holds.

DELETE(T,k)(T, k). Let i=r(k)i = r(k). If kk is not in the table, line L13 returns and nothing changes. Otherwise keys[i]=kkeys[i] = k. The loop of line L14 runs j=i,…,n−2j = i, \ldots, n - 2 and copies cell j+1j + 1 to cell jj. Cell j+1j + 1 has not yet been written by the loop, so after it, cell jj holds the old cell j+1j + 1 for i≤j≤n−2i \le j \le n - 2. Line L15 sets the count to n−1n - 1. The new key sequence is the old one without keys[i]keys[i], which is still strictly increasing, and the pairs are the old pairs without the pair of kk. So (I) holds.

Order operations. Under (I), keys[0]keys[0] is the least key, keys[n−1]keys[n-1] the greatest, and keys[i]keys[i] has exactly ii smaller keys, so it is select(i)(i). For ceiling, keys[r(k)]keys[r(k)] is the least key ≥k\ge k, since all earlier keys are less than kk. For floor, if keys[r(k)]=kkeys[r(k)] = k it is the answer; otherwise every key ≥k\ge k is greater than kk, and the largest key less than kk is keys[r(k)−1]keys[r(k) - 1]. The keys in [lo,hi][lo, hi] are those with index from r(lo)r(lo) to r(hi)−1r(hi) - 1, plus keys[r(hi)]keys[r(hi)] if it equals hihi; their count is r(hi)−r(lo)r(hi) - r(lo), plus 1 in that case. ■\blacksquare

Back to Binary search symbol table