Algorithm Search⌘K

Proofs: Linear probing

These are the human proofs of Linear probing, at full length.

Linear probing with backward-shift deletion is a correct dictionary full

This proof shows: correctness, termination.

All indices are taken modulo mm. For slots aa and bb, the cyclic path [a,b][a, b] is the list a,a+1,…,ba, a+1, \ldots, b, and (a,b](a, b] is the same list without aa. A slot ss is in (a,b](a, b] exactly when 0<(s−a) mod m≤(b−a) mod m0 < (s - a) \bmod m \le (b - a) \bmod m. A table is valid when:

(V1) the stored keys are distinct; (V2) at least one slot is EMPTY; (P) for each key xx at slot pp, every slot of the path [h(x),p][h(x), p] other than pp is full.

The empty table is valid. We show that each operation, run on a valid table, ends, gives the right result, and leaves a valid table that stores the right set.

Lemma 1 (SEARCH). On a valid table, SEARCH(T,k)(T, k) ends after at most mm probes. It returns the slot of kk if kk is stored, and NIL otherwise.

Proof. The loop of line L8 visits h(k),h(k)+1,…h(k), h(k)+1, \ldots and stops at the first EMPTY slot or at kk. By (V2) an EMPTY slot exists, so the loop ends within mm probes. It returns jj only when T[j]=kT[j] = k (line L9), and by (V1) that slot is the only one that holds kk. If kk is stored at pp, then by (P) the slots of [h(k),p][h(k), p] before pp are full and do not hold kk, so the loop reaches pp and returns it. If kk is not stored, line L9 never succeeds, and the loop ends at an EMPTY slot with NIL. □\square

Lemma 2 (INSERT). On a valid table with n+1<mn + 1 < m when kk is new, INSERT(T,k)(T, k) ends. If kk is stored, it changes nothing in the key layout and returns the slot of kk. Otherwise it stores kk, and the table stays valid.

Proof. Lines L1 to L4 are the loop of SEARCH, so by Lemma 1 they end, and they return at line L3 exactly when kk is stored. Otherwise the loop stops at the first EMPTY slot ee of the path from h(k)h(k), and every slot of [h(k),e][h(k), e] before ee is full. Line L5 puts kk at ee. Then (V1) holds because kk was absent. (V2) holds because the table now has n+1<mn + 1 < m keys. (P) holds for kk by the choice of ee. For another key xx at pp, the slots of [h(x),p][h(x), p] before pp were full and are still full, because line L5 only fills a slot. □\square

Lemma 3 (DELETE). On a valid table, DELETE(T,k)(T, k) ends. If kk is not stored, nothing changes. Otherwise the result is a valid table that stores the old keys without kk.

Proof. By Lemma 1, line L12 finds kk at slot i0i_0, or returns NIL and line L13 ends the call. Assume kk is at i0i_0. Before line L14 the table had at least one EMPTY slot by (V2), and i0i_0 was full, so the first EMPTY slot ee after i0i_0 exists, with e≠i0e \ne i_0. Call the slots of (i0,e)(i_0, e), which are all full at the start, the run. We write T0T_0 for the table before line L14.

Keys outside the run are not affected. Let xx be a key of T0T_0 at a slot pp that is neither i0i_0 nor in the run. The path [h(x),p][h(x), p] of xx has only full slots of T0T_0 before pp, so it does not contain ee. A cyclic path that does not contain ee and ends at pp, which is outside [i0,e][i_0, e], cannot contain a slot of [i0,e)[i_0, e): to leave this range going forward it must pass ee. So the path of xx avoids i0i_0 and the run. The loop below changes only slots of {i0}∪\{i_0\} \cup run, and pp is not among them, so xx keeps (P).

The loop invariant. At each test of line L18, with the current values of ii and jj:

(D1) jj is in [i0,e][i_0, e], and ii is in [i0,j)[i_0, j), so ii comes before jj on the path from i0i_0. (D2) Of the slots of [i0,e)[i_0, e), exactly ii is EMPTY; the others are full. (D3) Each slot of (j,e)(j, e) holds the same key as in T0T_0. (D4) Each key at a slot of [i0,j)[i_0, j) satisfies (P) in the current table. (D5) The keys of the current table are the keys of T0T_0 without kk.

The first time, line L17 has set j=i0+1j = i_0 + 1 and i=i0i = i_0. Then (D1) holds, (D2) holds because line L14 emptied i0i_0 and the run is full, (D3) holds because only i0i_0 changed, (D4) holds because the only slot of [i0,i0+1)[i_0, i_0 + 1) is the EMPTY slot i0i_0, and (D5) holds by line L14.

Step. Assume (D1) to (D5) at the test. If $T[j] = $ EMPTY, line L18 returns; we treat this case at the end. Otherwise jj is in the run by (D2) and (D1), so by (D3) it holds a key xx of T0T_0, with home r=h(x)r = h(x) (line L19). In T0T_0 the path [r,j][r, j] had only full slots before jj, so it does not contain ee.

  • Case 1: rr is in (i,j](i, j]. Then the path [r,j][r, j] lies in (i,j](i, j], so it avoids the hole ii. Its slots other than jj lie in [i0,e)[i_0, e) (the path avoids ee and ends at j∈[i0,e)j \in [i_0, e)), so by (D2) they are full now. Hence xx satisfies (P), and line L20 leaves it. Now (D4) holds for [i0,j][i_0, j]. After line L17 increases jj by one, (D1) to (D5) hold at the next test.
  • Case 2: rr is not in (i,j](i, j]. The path [r,j][r, j] contains ii: it ends at jj, it does not start in (i,j](i, j], and ii is the slot just before (i,j](i, j]. Line L21 moves xx to ii. The path [r,i][r, i] is the part of [r,j][r, j] up to ii, so its slots before ii were full in T0T_0 and differ from ii. They are full now: those in [i0,e)[i_0, e) by (D2), and the others because the loop changes no slot outside [i0,e)[i_0, e). So xx satisfies (P) at ii. Slot jj becomes the only EMPTY slot of [i0,e)[i_0, e), so (D2) holds with i←ji \leftarrow j. The other keys at slots of [i0,j)[i_0, j) keep (P): the path of such a key at qq ends at qq, which comes before jj on the path from i0i_0, and its start is not in (q,e)(q, e), else the path would pass ee, which is impossible as above. So the path does not contain jj, and emptying jj does not break it. Thus (D4) holds for [i0,j][i_0, j] with the new hole at jj. (D3) and (D5) hold, since only xx moved. After line L17, all five facts hold at the next test.

Termination. Each pass of the loop increases jj by exactly one along the path from i0i_0, and by (D1) jj never passes ee. Slot ee is EMPTY when it is reached, because by (D3) and the definition of ee the loop changes no slot outside [i0,e)[i_0, e). So the loop ends after at most (e−i0) mod m≤m−1(e - i_0) \bmod m \le m - 1 passes.

End. When line L18 returns, j=ej = e. By (D4) every key in [i0,e)[i_0, e) satisfies (P), and by the first part every key outside [i0,e][i_0, e] does too. Slot ee is EMPTY. So (P) holds for the whole table. (V1) holds by (D5), and (V2) holds because the hole ii is EMPTY. By (D5) the table stores the old keys without kk. □\square

Theorem. Starting from the empty table, every sequence of INSERT (each new key with n+1<mn + 1 < m), SEARCH and DELETE operations ends, each SEARCH returns the slot of its key exactly when the key is in the current set, and the table stays valid.

Proof. By induction on the number of operations. The empty table is valid and stores the empty set. If the table is valid and stores the current set before an operation, Lemmas 1, 2 and 3 show that the operation ends, gives the right answer, and leaves a valid table that stores the new set. ■\blacksquare

Back to Linear probing