Algorithmics study and revision notes
2026-08-22
A dictionary stores key-value associations and supports search, insertion, and deletion by key. Direct addressing is ideal when keys already lie in a small universe: store the value for key in array cell . When the possible key universe is enormous compared with the number of stored keys, that array wastes space.
Hashing maps a large universe into a manageable table. The price is unavoidable collisions: different keys can map to the same cell. A hash table is correct because it resolves collisions and checks actual key equality, not because its hash function somehow prevents all collisions.
After studying this note, you should be able to:
A dictionary maintains a set of entries , normally with unique keys. Its abstract operations are:
insert_or_replace(D, key, value)
search(D, key)
delete(D, key)
The model should specify key equality and mutability. If a key changes after insertion while its stored hash position does not, later search may fail.
A hash code maps a key to a large integer, often a machine word. A separate compression step maps that integer to a table index in . For example,
This separation matters when resizing: changing changes table indices even when hash codes remain fixed.
Equal keys must produce equal hash codes. For expected performance, the indexed values of the anticipated keys should be spread across the table without strong patterns. Fast mixing is useful, but it is not the same goal as cryptographic preimage or collision resistance.
For composite objects, combine every equality-relevant field in a defined order. A string hash, for example, can update an integer accumulator once per character and then apply a table-specific compression or universal-hashing step. Test a proposed function on representative and adversarial-looking data; visual uniformity alone is not a proof.
Each table cell stores a bucket containing all entries whose index equals that cell. A bucket may be a list, compact array, or another small dictionary.
With entries and buckets, the load factor is
Under simple uniform hashing, a bucket has expected length , and search, insertion, and deletion take expected time. Chaining permits and makes deletion direct, but pointer-heavy buckets can have poor locality.
Correct search first selects the bucket, then compares the query key with each candidate’s actual key. Comparing hash codes alone is incorrect because unequal keys can share a code.
Open addressing stores every entry directly in the table. For key , a probe sequence
specifies cells to inspect until the key or an available cell is found.
Search must follow exactly the insertion probe sequence. Deleting an item by marking its cell empty can break a later search whose probe path crosses that cell. Use a tombstone that search passes but insertion may reuse, or rebuild the affected table.
Open addressing requires . Under the idealised uniform-probing model, expected probes for an unsuccessful search are at most
and for a successful search at most
Real linear probing has correlated probes, so these formulas are a model, not a guarantee. Performance rises sharply as the table becomes full.
When load passes a chosen threshold, allocate a geometrically larger table and reinsert every live entry. Entries must be reinserted because their indices depend on the new size; copying cells to equal indices is not enough.
A resize costs , but geometric growth makes a long sequence of insertions amortised under the hashing assumptions. Each entry can be charged a small amount on insertion to pay for the occasional rebuild. Tombstones may also trigger a same-size rebuild even when the live load is moderate.
Always distinguish the two probability layers:
An ordinary hash table still has worst-case search if many keys land together.
Suppose distinct keys are hashed independently and uniformly into cells. Each unordered key pair collides with probability . By linearity of expectation,
For , the exact no-collision probability is
When is small relative to , taking logarithms and retaining the leading term gives the birthday approximation
The probability of at least one collision reaches about one half near
For any particular cell, the probability of remaining empty is . Hence
Define the reported collision metric. “Colliding pairs,” “keys beyond the first in an occupied cell,” and “number of occupied cells with load at least two” are different quantities.
A fixed deterministic hash can perform badly on a carefully selected key set. Universal hashing chooses one function randomly from a family after the key set is fixed. A family mapping universe to cells is universal if, for every distinct ,
A standard construction chooses a prime larger than every encoded key, chooses uniformly from and uniformly from , and uses
The random parameters are selected once per table and stored with it. For two distinct keys, the affine map modulo randomises their relative residues sufficiently to bound their chance of landing in the same final bucket. The guarantee is about collision probability over the function choice; it does not say one realised table has no collisions.
More explicitly, for distinct , the pair
is uniform over ordered pairs of distinct residues. After fixing the first residue, at most of the other residues have the same remainder modulo . Hence the collision probability is at most .
A Bloom filter represents approximate membership using an -bit array and hash-derived positions.
insert(x):
for each position h_i(x): set bit h_i(x)
possibly_contains(x):
return every bit h_i(x) is 1
If any queried bit is zero, the key is definitely absent. If all are one, it is possibly present. With consistent hashing and no unsupported deletion, inserted keys have no false negatives; unrelated keys can be false positives.
Under the standard model in which the selected positions are independent and uniform, a particular bit remains zero with probability
Under the usual independence approximation, all queried positions are one with probability
For fixed bits per item , this expression is minimised near
With ten bits per item, , so seven hashes are natural:
This is a model prediction. Correlated or poorly distributed hashes can produce worse results. Compare measured false positives against the prediction on queries known not to be in the inserted set.
Practical filters often derive positions from two base hashes rather than compute unrelated hashes. This reduces hashing cost and can work well, but the independence calculation remains a model to validate empirically for the chosen scheme and data.
A standard Bloom filter cannot safely delete: clearing a bit shared with another item may create a false negative. A counting Bloom filter replaces bits with small counters, at greater space cost.
These concepts share a name but solve different problems:
A password salt is a public, unique random value stored with the password verifier. It prevents identical passwords from sharing one stored result and defeats one precomputed table across many accounts. It does not turn a fast general hash into a suitable password-hashing scheme.
Using a cryptographic digest and then reducing it modulo can distribute table indices well, but usually costs more CPU than a noncryptographic table hash. Performance and threat model should decide whether that cost is justified.
Search programs repeatedly modify structured states such as game boards. Zobrist hashing assigns a random machine word to every position-piece combination and XORs the words for all occupied positions:
For a Tic-Tac-Toe move placing X in cell 4, update in constant time with
Moving or replacing a piece XORs out its old contribution and XORs in its new one. The hash is an efficient fingerprint for transposition tables, not a proof of state equality; store enough state information to detect the rare collision.
A perfect hash function has no collisions on one fixed key set. Two-level randomized schemes can build a static dictionary with expected linear space and construction time and worst-case constant lookup. A minimal perfect hash maps fixed keys into exactly indices, but normally needs extra storage to verify whether an arbitrary query belongs to the original set.
Locality-sensitive hashing deliberately gives similar objects an elevated chance of colliding. That goal is nearly the opposite of ordinary table hashing and is useful for approximate similarity search. It requires a defined distance or similarity family and its own probability analysis.
Let , use , and resolve collisions by linear probing.
For a separate experiment with and uniformly hashed keys,
The birthday estimate predicts only probability of no collision. A collision is therefore unsurprising even though only one percent of the table’s capacity is used.
| Structure | Lookup | Insert | Delete | Main assumptions |
|---|---|---|---|---|
| Direct-address table | worst case | small integer universe | ||
| Chained hash table | expected | expected amortised | expected | distributed hashes and resizing |
| Open addressing | expected at controlled | expected amortised | expected | valid probe sequence and tombstones |
| Balanced search tree | worst case | comparable keys; preserves order | ||
| Bloom filter | unsupported normally | approximate membership only |
Hash tables do not preserve order. Use a search tree or another ordered index for predecessor, successor, sorted iteration, and range queries.
dict
is the natural dictionary baseline and preserves insertion order. Its
public interface does not expose probe counts or load thresholds, so use
a teaching implementation when those internal measurements are the
experiment.flat_hash_map
is a production Swiss-table implementation optimised for locality.
Rehashing invalidates iterators, references, and pointers to elements;
use node_hash_map when pointer stability is required.HashMap
uses a randomly seeded hasher and a SwissTable-derived layout. Iteration
order is unspecified, and replacing the default hasher trades security
and speed properties that should be stated.BloomFilter
exposes expected insertions and target false-positive probability
directly. Inserting far beyond that estimate saturates the filter and
sharply worsens its false-positive rate; ordinary deletion is
unsupported.