Hash Tables
Hash functions and the division and multiplication methods, the load factor, collision resolution by separate chaining and by open addressing (linear probing, quadratic probing, double hashing), primary and secondary clustering, deletion with tombstones, expected probe counts, rehashing, Python's dict and set, and typical hashing patterns in algorithms.
The idea
A hash table stores key–value pairs in an array of buckets. A hash function maps each key to an index , giving insert, search and delete in expected time.
Hash functions
A good hash function is fast, deterministic, and spreads keys uniformly.
| Method | Formula | Notes |
|---|---|---|
| Division | choose prime, not near a power of 2 | |
| Multiplication | , | can be a power of 2; works well |
| Strings | polynomial rolling hash | also used in Rabin–Karp |
| Universal hashing | pick at random from a family | guarantees good expected behaviour for any input |
The load factor is (keys per bucket).
Collision resolution
Collisions are unavoidable once is moderate: with buckets, a collision becomes likely after about insertions (the birthday paradox).
Separate chaining
Each bucket holds a linked list (or dynamic array) of entries.
- Insert ; search and delete expected.
- Works for any , degrades gracefully; worst case if everything collides.
Open addressing
All entries live in the array; on a collision, follow a probe sequence until an empty slot is found.
| Probing | Drawback | |
|---|---|---|
| Linear | primary clustering: long runs of occupied slots grow | |
| Quadratic | secondary clustering: keys with the same share a sequence; may not visit every slot | |
| Double hashing | best spread; needs coprime to |
- Requires ; performance collapses as .
- Deletion uses a tombstone ("deleted") marker: searches skip over it, insertions may reuse it.
- Expected probes (uniform hashing): unsuccessful ; successful . Linear probing: about and .
Rehashing
When crosses a threshold (Python dicts resize at about 2/3 full), allocate a larger table (typically double) and re-insert everything: for that operation but amortised per insertion.
Python's dict and set
- Both are open-addressing hash tables; average lookup, insertion and deletion.
- Keys/elements must be hashable (implement
__hash__and__eq__consistently; equal objects must have equal hashes). Mutable built-ins are unhashable. - Since Python 3.7, dicts preserve insertion order.
collections.Counteranddefaultdictare dict subclasses for counting and grouping.
| Operation | list | set / dict |
|---|---|---|
x in s | average | |
| insert | at end | average |
| delete by value/key | average | |
| ordered iteration | yes | dict: insertion order; set: no order |
Hashing patterns in algorithms
Hash tables vs balanced BSTs: hashing gives expected but no ordering; BSTs give guaranteed plus sorted iteration, range queries, min/max and predecessor/successor.
Quick revision
Practice questions
GATE-style questions: 3 one-mark and 6 two-mark. Wrong MCQs cost ⅓ of their marks in the real exam; MSQ and NAT have no negative marking. Attempt each before opening the solution.
Keys 10, 17, 24, 3 are inserted in this order into an initially empty hash table of size 7 using and linear probing. The index at which key 3 is stored is ______.
With separate chaining and simple uniform hashing, the expected time for a search in a table with load factor is
Under the uniform hashing assumption, the expected number of probes in an unsuccessful search in an open-addressing table with load factor is at most ______.
Keys 22, 33, 44 are inserted in order into an empty table of size 11 using quadratic probing for . The index of key 44 is ______.
Which statements about collision resolution are correct?
One or more options are correct.
Which of these can be used as a key in a Python dict?
20 keys are hashed independently and uniformly into 100 slots. The expected number of colliding pairs of keys (pairs that land in the same slot) is ______.
Double hashing uses with and . For , the slot probed at (after a collision at ) is ______.
What does this program print?
seen = set()
pairs = 0
for x in [3, 1, 4, 1, 5, 9, 2, 6, 5, 3]:
if 10 - x in seen:
pairs += 1
seen.add(x)
print(pairs)