DSA Notes
Master open addressing for hash tables — linear probing, quadratic probing, double hashing, the clustering problem, deletion with tombstones, and complete implementations.
How Open Addressing Works
In open addressing, all elements live directly inside the hash table array. When a collision occurs, we systematically search for the next available slot using a probe sequence. There are no linked lists, no pointers — just the flat array.
The probe sequence for key k is: h(k,0), h(k,1), h(k,2), ... where h(k,i) gives the i-th slot to check. Different probing methods define different sequences.
Linear Probing
The simplest approach: if slot h(k) is occupied, try h(k)+1, then h(k)+2, and so on (wrapping around).
h(k, i) = (h(k) + i) mod m
| Table size m=7. Insert keys | 7, 14, 21, 3 |
| h(7) = 7 % 7 = 0 | slot 0 free → place 7 |
| h(14) = 14 % 7 = 0 | slot 0 taken → try 1 → free → place 14 |
| h(21) = 21 % 7 = 0 | slot 0 taken → 1 taken → try 2 → free → place 21 |
| h(3) = 3 % 7 = 3 | slot 3 free → place 3 |
| Table | [7, 14, 21, 3, _, _, _] |
The Clustering Problem
Linear probing suffers from primary clustering: occupied slots form contiguous blocks. New keys hashing near a cluster join and extend it, making the cluster grow faster.
Despite clustering, linear probing has excellent cache performance (sequential memory access) and is the fastest method for low load factors (α < 0.5).
Quadratic Probing
Spreads probes more by using quadratic steps: h(k, i) = (h(k) + c₁i + c₂i²) mod m
Common choice: h(k, i) = (h(k) + i²) mod m
| Probe 0 | slot 3 |
| Probe 1 | slot (3 + 1) % 11 = 4 |
| Probe 2 | slot (3 + 4) % 11 = 7 |
| Probe 3 | slot (3 + 9) % 11 = 1 |
| Probe 4 | slot (3 + 16) % 11 = 8 |
Eliminates primary clustering — different initial hashes produce different probe sequences. However, keys with the same hash still follow the same quadratic sequence (secondary clustering).
Caveat: Quadratic probing does not guarantee visiting all slots. For full coverage, table size must be prime and α < 0.5, OR use (h + i(i+1)/2) mod 2^k with power-of-2 table size.
Double Hashing
Uses a second hash function to determine the probe step size:
h(k, i) = (h₁(k) + i × h₂(k)) mod m
# h1(k) = k % m
# h2(k) = 1 + (k % (m-1)) — ensures h2 is never 0
# Key=14, m=7:
# h1(14) = 0, h2(14) = 1 + (14 % 6) = 3
# Probe sequence: 0, 3, 6, 2, 5, 1, 4 — visits ALL slots!No clustering at all — each key has its own unique probe sequence. This gives the best distribution but costs an extra hash computation per probe.
Complete Python Implementation
The Tombstone Problem
Deletion in open addressing cannot simply empty a slot — that would break probe chains for keys inserted after the deleted one. Solution: mark deleted slots with a tombstone (DELETED sentinel).
| Before delete(14) | [7, 14, 21, 3, _, _, _] |
| Naive empty slot | [7, _, 21, 3, _, _, _] |
| Search for 21: h(21)=0 | taken(7) → probe 1 → EMPTY → "not found"! WRONG! |
| With tombstone | [7, DEL, 21, 3, _, _, _] |
| Search for 21: h(21)=0 | taken(7) → probe 1 → DELETED (continue) → found 21! ✓ |
Tombstone drawback: Over time, many tombstones accumulate and degrade search performance. Periodic rehashing (resize without growing) cleans them up.
Performance Comparison
| Method | Avg Probes (Success, α=0.5) | Avg Probes (Failure, α=0.5) | Clustering |
|---|---|---|---|
| Linear | 1.5 | 2.5 | Primary + Secondary |
| Quadratic | 1.44 | 2.19 | Secondary only |
| Double Hashing | 1.39 | 2.0 | None |
At α = 0.7:
| Method | Success | Failure |
|---|---|---|
| Linear | 2.17 | 6.06 |
| Quadratic | 1.85 | 3.82 |
| Double | 1.72 | 3.33 |
C++ Implementation (Linear Probing)
Real-World Usage
- Python dict: Uses open addressing with a custom probe sequence (perturbed by hash bits)
- Rust HashMap: Robin Hood hashing (linear probing variant with displacement)
- Google's dense_hash_map: Quadratic probing optimized for cache
- Game engines: Linear probing preferred for cache performance with small load factors
Key Takeaways
Open addressing stores everything in a flat array — excellent cache locality, no pointer overhead, but requires careful load factor management (keep α < 0.7). Linear probing is fastest but clusters. Quadratic probing eliminates primary clustering. Double hashing eliminates all clustering but costs an extra hash. Tombstones are necessary for deletion but accumulate — periodic rehashing is the cure. Choose open addressing when memory efficiency and cache performance are priorities.
Exam Focus
Revise definitions, diagrams, examples, and short-answer points for Open Addressing.
Interview Use
Prepare one clear explanation, one practical example, and one common mistake for this Data Structures & Algorithms topic.
Search Terms
data-structures-algorithms, data structures & algorithms, data, structures, algorithms, hashing, open, addressing
Related Data Structures & Algorithms Topics