Algorithm Search⌘K

Proofs: Packed memory array

These are the human proofs of Packed memory array, at full length.

The packed memory array keeps the keys sorted, always has room, and every operation ends full

This proof shows: correctness, termination.

We use the pseudocode of the page. The array has capacity cc, a power of two, and segments of SS cells with SS dividing cc, so the windows at each height i≤Hi \le H are well defined: the aligned runs of S2iS 2^i cells, and the window at height HH is the whole array. The thresholds satisfy τi≤τ0=1\tau_i \le \tau_0 = 1 for all ii. Let the key sequence be the keys of slots read from left to right, skipping gaps.

Invariant (I). The key sequence is strictly increasing, and it is the set stored.

Lemma 1 (search). If (I) holds, the binary search of line L1 returns the position of the largest key at most kk, or −1-1 if there is none.

Proof. The search keeps lo≤hilo \le hi and the property: every key at a position before lolo is at most kk, and every key at a position at or after hihi is larger than kk. This holds at the start with lo=0lo = 0, hi=chi = c. In a pass, let midmid be the midpoint and jj the first position at or after midmid, before hihi, that holds a key. If there is none, the cells from midmid to hihi are gaps, so setting hi←midhi \leftarrow mid keeps the property. If the key xx at jj is at most kk, then by (I) all keys before jj are smaller than xx, so setting lo←j+1lo \leftarrow j + 1 keeps it. If x>kx > k, all keys after jj are larger, and the cells from midmid to jj are gaps, so setting hi←midhi \leftarrow mid keeps it. Each pass makes hi−lohi - lo smaller, since mid<himid < hi and j+1>mid≥loj + 1 > mid \ge lo, so the loop ends with lo=hilo = hi. Then the keys before lolo are exactly the keys at most kk, and the last of them, found by the scan to the left over gaps, is the largest such key. □\square

Lemma 2 (insert). If (I) holds and kk is absent, INSERT adds kk, and (I) holds after it.

Proof. Let pp be the position from Lemma 1 (use segment 0 if p=−1p = -1). In line L5, the window WW holds position pp (or position 0). All keys before WW are at positions before pp or at pp, so they are at most the predecessor of kk, which is less than kk; all keys after WW are after pp, so they are larger than kk by Lemma 1. REBALANCE (line L12) collects the keys of WW in order, adds kk at its place, and writes them back in order. The test of line L4 gives keys(W)+1≤τi∣W∣≤∣W∣\mathrm{keys}(W) + 1 \le \tau_i |W| \le |W|, so there are enough cells, and the spread positions ⌊t∣W∣/m⌋\lfloor t |W| / m \rfloor for t=0,…,m−1t = 0, \ldots, m - 1 are distinct and increasing because ∣W∣≥m|W| \ge m. So the new key sequence is the old keys before WW, then the keys of WW with kk, then the old keys after WW: strictly increasing, with kk added. If no window passes the test, RESIZE (line L13) collects all keys in order with kk added, and picks a capacity c′≥22 m>mc' \ge 2\sqrt{2}\, m > m, so the spread fits, and it is in order. □\square

Lemma 3 (delete). If (I) holds, DELETE removes kk if it is present and changes nothing otherwise, and (I) holds after it.

Proof. By Lemma 1, line L7 finds kk if it is present. Making its cell a gap removes kk from the key sequence and keeps it increasing. A later REBALANCE of a window rewrites its keys in the same order in the same window, which keeps (I), as does a RESIZE, by the argument of Lemma 2 with no key added. □\square

Range and membership. By (I), range(a, b) starts at the first key at least aa, found from Lemma 1, and reads the keys in increasing order, stopping at the first key larger than bb; so it returns exactly the keys in [a,b][a, b]. contains(k) is true exactly when the predecessor of kk is kk.

Termination. The loops of lines L2 and L8 run over at most H+1H + 1 heights. REBALANCE and RESIZE are single passes over a finite list; the capacity loop in RESIZE doubles c′c' until it passes a finite bound. The binary search ends by Lemma 1. So every operation ends.

The structure stays valid. After a RESIZE, c′c' is a power of two and SS is recomputed as a power of two at most c′c', so SS divides c′c'. Every window holds at most its size in keys, because a write only puts keys at distinct cells.

The empty array satisfies (I), and by Lemmas 2 and 3, every update keeps it; inserting a present key changes nothing. So (I) holds after every sequence of operations, and the answers of all queries are right. ■\blacksquare

Back to Packed memory array