Proofs: Direct-address table
These are the human proofs of Direct-address table, at full length.
The direct-address table and its sparse version implement a dictionary full
This proof shows: correctness.
Theorem 1 (plain table). Start from the table with for all and , and apply any sequence of DA-SEARCH, DA-INSERT and DA-DELETE with keys in and values other than NIL. After each operation the invariant holds:
- (D1) is the map that the same sequence gives on an abstract dictionary;
- (D2) .
Each search returns the value of its key in that map, or NIL.
Proof. At the start every slot is NIL, is the empty map and , so (D1) and (D2) hold. Assume they hold before an operation on key , and let be the map at that time.
Search. Line L1 returns . If is a key of , then by (D1). If not, then no pair of has key , so by (D1). No field changes.
Insert. Only slot and change. Line L3 sets , so the new set of pairs is with the pair of removed and added, which is the dictionary insert. If was NIL, the number of slots that are not NIL grows by one, and line L2 adds one to . If was not NIL, that number does not change, and line L2 does not change . So (D2) holds.
Delete. Only slot and change. Line L5 sets , so the new set of pairs is without the pair of , if there was one, which is the dictionary delete. Line L4 lowers by one exactly when the slot was not NIL before, which is exactly when the number of slots that are not NIL drops by one. So (D2) holds.
By induction on the number of operations, the claim holds after each one.
Theorem 2 (sparse version). Let the arrays , and have arbitrary contents at creation, and let . Apply any sequence of SP-SEARCH, SP-INSERT and SP-DELETE with keys in . Let be the set of keys of the abstract dictionary after the same sequence, with values . After each operation:
- (S1) are distinct, and ;
- (S2) for each ;
- (S3) for each .
Moreover, under (S1) and (S2), VALID is true if and only if , whatever the contents of the slots that were never written. So each search returns or NIL.
Proof. First the claim on VALID. Assume (S1) and (S2). If , then by (S1) for some , and by (S2) . So line L6 reads and line L7 finds and , and returns true. Conversely, if line L7 returns true, then satisfies and , so by (S1). This argument uses only the slots , which (S1) describes, and not the content of beyond the value read, so garbage cannot make VALID true for a key outside .
At creation and , so (S1) to (S3) hold, the last two vacuously. Assume they hold before an operation on key .
Search. By the claim, line L8 returns by (S3) when , and NIL otherwise. No field changes.
Insert, . VALID is true, so line L10 is skipped, and line L11 sets . The set does not change and the new value of is , so (S1) to (S3) hold.
Insert, . Line L10 writes and , then raises by one. Since , the keys are distinct and form , which is (S1). For the old relation still holds, since the only slot of that changed is , and differs from each with . Together with this gives (S2). Line L11 sets , and the other values are unchanged, so (S3) holds. The stack never overflows: by (S1), .
Delete, . VALID is false and nothing changes, which is the dictionary delete of an absent key.
Delete, . Let , so by (S2), and let . Line L14 sets and and lowers by one. If , then , and the effect is to drop the last stack entry, which is . If , the entry at position is replaced by the last entry , and the last position leaves the used part. In both cases holds the old keys without , each once, which is (S1) for . For (S2), every key other than keeps its position and its slot of , and , if it is still stored, is now at position with . The values of the other keys are unchanged, so (S3) holds.
By induction on the number of operations, (S1) to (S3) hold after each one, and with the claim on VALID each operation answers as the dictionary does.