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 . For slots and , the cyclic path is the list , and is the same list without . A slot is in exactly when . A table is valid when:
(V1) the stored keys are distinct; (V2) at least one slot is EMPTY; (P) for each key at slot , every slot of the path other than 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 ends after at most probes. It returns the slot of if is stored, and NIL otherwise.
Proof. The loop of line L8 visits and stops at the first EMPTY slot or at . By (V2) an EMPTY slot exists, so the loop ends within probes. It returns only when (line L9), and by (V1) that slot is the only one that holds . If is stored at , then by (P) the slots of before are full and do not hold , so the loop reaches and returns it. If is not stored, line L9 never succeeds, and the loop ends at an EMPTY slot with NIL.
Lemma 2 (INSERT). On a valid table with when is new, INSERT ends. If is stored, it changes nothing in the key layout and returns the slot of . Otherwise it stores , 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 is stored. Otherwise the loop stops at the first EMPTY slot of the path from , and every slot of before is full. Line L5 puts at . Then (V1) holds because was absent. (V2) holds because the table now has keys. (P) holds for by the choice of . For another key at , the slots of before were full and are still full, because line L5 only fills a slot.
Lemma 3 (DELETE). On a valid table, DELETE ends. If is not stored, nothing changes. Otherwise the result is a valid table that stores the old keys without .
Proof. By Lemma 1, line L12 finds at slot , or returns NIL and line L13 ends the call. Assume is at . Before line L14 the table had at least one EMPTY slot by (V2), and was full, so the first EMPTY slot after exists, with . Call the slots of , which are all full at the start, the run. We write for the table before line L14.
Keys outside the run are not affected. Let be a key of at a slot that is neither nor in the run. The path of has only full slots of before , so it does not contain . A cyclic path that does not contain and ends at , which is outside , cannot contain a slot of : to leave this range going forward it must pass . So the path of avoids and the run. The loop below changes only slots of run, and is not among them, so keeps (P).
The loop invariant. At each test of line L18, with the current values of and :
(D1) is in , and is in , so comes before on the path from . (D2) Of the slots of , exactly is EMPTY; the others are full. (D3) Each slot of holds the same key as in . (D4) Each key at a slot of satisfies (P) in the current table. (D5) The keys of the current table are the keys of without .
The first time, line L17 has set and . Then (D1) holds, (D2) holds because line L14 emptied and the run is full, (D3) holds because only changed, (D4) holds because the only slot of is the EMPTY slot , 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 is in the run by (D2) and (D1), so by (D3) it holds a key of , with home (line L19). In the path had only full slots before , so it does not contain .
- Case 1: is in . Then the path lies in , so it avoids the hole . Its slots other than lie in (the path avoids and ends at ), so by (D2) they are full now. Hence satisfies (P), and line L20 leaves it. Now (D4) holds for . After line L17 increases by one, (D1) to (D5) hold at the next test.
- Case 2: is not in . The path contains : it ends at , it does not start in , and is the slot just before . Line L21 moves to . The path is the part of up to , so its slots before were full in and differ from . They are full now: those in by (D2), and the others because the loop changes no slot outside . So satisfies (P) at . Slot becomes the only EMPTY slot of , so (D2) holds with . The other keys at slots of keep (P): the path of such a key at ends at , which comes before on the path from , and its start is not in , else the path would pass , which is impossible as above. So the path does not contain , and emptying does not break it. Thus (D4) holds for with the new hole at . (D3) and (D5) hold, since only moved. After line L17, all five facts hold at the next test.
Termination. Each pass of the loop increases by exactly one along the path from , and by (D1) never passes . Slot is EMPTY when it is reached, because by (D3) and the definition of the loop changes no slot outside . So the loop ends after at most passes.
End. When line L18 returns, . By (D4) every key in satisfies (P), and by the first part every key outside does too. Slot is EMPTY. So (P) holds for the whole table. (V1) holds by (D5), and (V2) holds because the hole is EMPTY. By (D5) the table stores the old keys without .
Theorem. Starting from the empty table, every sequence of INSERT (each new key with ), 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.