Lecture 15
What Is a Hash Table?
A hash table stores key-value pairs and supports insert, search, and delete in O(1) amortized time. The central idea: apply a hash function to the key to compute a bucket index, then store the pair in that bucket.
When two keys map to the same bucket — a collision — the table needs a strategy to handle it. This lecture covers two: separate chaining and open addressing.
Hash Functions
A hash function maps a key to a non-negative integer. For integer keys, the simplest hash is modulo division:
Good hash functions distribute keys uniformly across buckets. Clustering keys into a few buckets turns O(1) lookups into O(n) list traversals.
Choose a prime number for the capacity. Prime capacities reduce clustering when keys share common factors with the capacity.
In C++, the % operator on negative integers produces a negative result. Guard against this:
((key % capacity) + capacity) % capacity.
Chaining
Separate chaining resolves collisions by keeping a linked list at each bucket. Every key that hashes to the same bucket is appended to that list.
insert walks the chain to check for an existing key. If found, it updates the value in place; if
not, it prepends a new Node in O(1). find and remove likewise walk only the chain for that
bucket — not the entire table.
Load Factor
The load factor α = n / m, where n is the number of stored keys and m is the number of buckets. At low load (α ≪ 1) chains are short and lookups are fast. As α grows, chains lengthen and performance approaches O(n).
A common rule: when α exceeds 0.75, resize to roughly double the capacity and rehash all existing keys. This keeps amortized O(1) performance.
Open Addressing
Open addressing stores all entries directly in the bucket array — no linked lists. When the home bucket is occupied, the table probes for the next available slot.
Linear probing tries successive indices:
The Deletion Problem
Deleting a slot by writing EMPTY breaks the probe chain: a later find stops at the gap and
misses any keys inserted beyond it. Instead, mark deleted slots with a tombstone (DELETED).
find skips tombstones; insert reuses them.
Never write EMPTY into a deleted slot in an open-addressing table. Future lookups will terminate early and report keys as missing even when they are present.
Chaining vs. Open Addressing
| Chaining | Open Addressing | |
|---|---|---|
| Storage | Nodes on heap | Flat array — cache-friendly |
| High load (α > 1) | Works — chains just grow longer | Fails — no room once array is full |
| Deletion | Splice the node | Tombstone required |
| Memory per entry | Extra pointer per node | Keys and values stored in-place |
Chaining handles high load gracefully. Open addressing is faster at low load due to cache locality.
Exercises
Q1.
Without running the code, predict what this program prints.
Q2.
Without running the code, trace the keys array after each operation and predict the output. The table uses linear probing with CAPACITY = 7.
Q3.
Each snippet has a bug related to hashing. Identify what's wrong.
Q4.
Implement insert and find for the chaining hash table below.
Q5.
Implement insert and find for the linear-probing hash table below.
Practice Problems
- Two Sum — Easy
- Group Anagrams — Medium
- Longest Consecutive Sequence — Medium
- Top K Frequent Elements — Medium