Algorithm Search⌘K

Proofs: Extendible hashing

These are the human proofs of Extendible hashing, at full length.

Extendible hashing keeps its invariant and implements a dictionary full

This proof shows: correctness, termination.

We write lowj(x)=x mod 2j\text{low}_j(x) = x \bmod 2^j, and P(T)P(T) for the set of pairs in the buckets that the directory points to. The invariant is the one of the page: (E1) each bucket BB has a pattern p(B)<2ℓ(B)p(B) < 2^{\ell(B)} with ℓ(B)≤d\ell(B) \le d, and dir[i]=B\text{dir}[i] = B exactly when lowℓ(B)(i)=p(B)\text{low}_{\ell(B)}(i) = p(B); (E2) each pair (k,v)(k, v) in BB has lowℓ(B)(h(k))=p(B)\text{low}_{\ell(B)}(h(k)) = p(B); (E3) the keys are distinct; (E4) n=∣P(T)∣n = |P(T)|; (E5) ∣B∣≤b|B| \le b unless ℓ(B)=W\ell(B) = W. The hash values are in {0,…,2W−1}\{0, \ldots, 2^W - 1\}.

Theorem. Start with d=0d = 0 and one empty bucket of depth 0, and apply any sequence of EH-INSERT, EH-SEARCH and EH-DELETE. Each operation ends, keeps (E1) to (E5), and keeps P(T)P(T) equal to the map that the same sequence gives on an abstract dictionary. Each search returns the value of its key in that map, or NIL.

Proof. At the start, the one bucket has pattern 0 of depth 0, and dir[0]\text{dir}[0] points to it, so (E1) holds; the bucket is empty, so (E2) to (E5) hold.

Step 1 (the right bucket). Assume (E1) and (E2), and let i=lowd(h(k))i = \text{low}_d(h(k)). If a pair with key kk is in a bucket BB, then by (E2) p(B)=lowℓ(B)(h(k))p(B) = \text{low}_{\ell(B)}(h(k)). Since ℓ(B)≤d\ell(B) \le d, lowℓ(B)(i)=lowℓ(B)(h(k))=p(B)\text{low}_{\ell(B)}(i) = \text{low}_{\ell(B)}(h(k)) = p(B), so by (E1) dir[i]=B\text{dir}[i] = B. Hence lines L1, L4 and L13 find the only bucket that can hold key kk, and if it holds no pair with key kk, then kk is not a key.

Step 2 (search and delete). By Step 1 and (E3), lines L2 and L3 return the value of kk or NIL. Lines L13 and L14 remove the only pair with key kk, if there is one, and lower nn by 1. Removing a pair keeps (E1) to (E5), and the map loses exactly kk.

Step 3 (doubling, line L7). Let the directory have 2d2^d entries with (E1), and set dir[i+2d]=dir[i]\text{dir}[i + 2^d] = \text{dir}[i] for 0≤i<2d0 \le i < 2^d, then d←d+1d \leftarrow d + 1. For 0≤i<2d+10 \le i < 2^{d+1}, the new dir[i]\text{dir}[i] is the old dir[lowd(i)]\text{dir}[\text{low}_d(i)]. For a bucket BB with ℓ(B)≤d\ell(B) \le d, the new dir[i]=B\text{dir}[i] = B exactly when lowℓ(B)(lowd(i))=p(B)\text{low}_{\ell(B)}(\text{low}_d(i)) = p(B), which is lowℓ(B)(i)=p(B)\text{low}_{\ell(B)}(i) = p(B). So (E1) holds for the new depth. No pair moves, so (E2) to (E5) hold.

Step 4 (split, lines L8 to L10). Let BB have depth ℓ<d\ell < d and pattern pp; line L7 ensures ℓ<d\ell < d before line L8, since it doubles when ℓ=d\ell = d. Lines L8 to L10 set both depths to ℓ+1\ell + 1, move to B′B' the pairs whose hash has bit ℓ\ell equal to 1, and move to B′B' the directory entries ii with dir[i]=B\text{dir}[i] = B and bit ℓ\ell of ii equal to 1. Give BB the pattern pp and B′B' the pattern p+2ℓp + 2^\ell, both with ℓ+1\ell + 1 bits. An index ii with lowℓ(i)=p\text{low}_\ell(i) = p has lowℓ+1(i)=p\text{low}_{\ell+1}(i) = p when bit ℓ\ell of ii is 0, and p+2ℓp + 2^\ell when it is 1. So after line L10, dir[i]=B\text{dir}[i] = B exactly when lowℓ+1(i)=p\text{low}_{\ell+1}(i) = p, and dir[i]=B′\text{dir}[i] = B' exactly when lowℓ+1(i)=p+2ℓ\text{low}_{\ell+1}(i) = p + 2^\ell, and ℓ+1≤d\ell + 1 \le d. The other buckets and their entries do not change, so (E1) holds. A pair of BB had lowℓ(h(k))=p\text{low}_\ell(h(k)) = p, and the same case split on bit ℓ\ell of h(k)h(k) shows that it now satisfies (E2) in BB or in B′B'. The set P(T)P(T) does not change, so (E3) and (E4) hold. The two new buckets have at most the old size of BB, and (E5) is used only at line L12.

Step 5 (insert). If line L5 finds kk, by Step 1 this is its only pair, and the value is replaced; nothing else changes. Else kk is not a key. While the bucket BB of kk is full and ℓ(B)<W\ell(B) < W, a round of the loop does Steps 3 and 4 and then, by Step 1 applied to the new state, line L11 finds the bucket where a pair with key kk must go. Each round raises the local depth of the bucket of kk by 1: after the split, kk's bucket is BB or B′B', both of depth ℓ(B)+1\ell(B) + 1. Since this depth is at most WW, the loop runs at most WW rounds and ends. At its end, the bucket BB of kk has fewer than bb pairs, or ℓ(B)=W\ell(B) = W. Line L12 adds (k,v)(k, v) to B=dir[lowd(h(k))]B = \text{dir}[\text{low}_d(h(k))]. By (E1), p(B)=lowℓ(B)(lowd(h(k)))=lowℓ(B)(h(k))p(B) = \text{low}_{\ell(B)}(\text{low}_d(h(k))) = \text{low}_{\ell(B)}(h(k)), so (E2) holds. kk was not a key, so (E3) holds, nn grows by 1, so (E4) holds, and BB now has at most bb pairs or depth WW, so (E5) holds. The map is the old map plus (k,v)(k, v).

By induction on the number of operations, the theorem holds. ■\blacksquare

Back to Extendible hashing