Hashing: chaining, open addressing and rehashing
The hash function, load factor, separate chaining, linear/quadratic probing, double hashing, lazy deletion and rehashing; step-by-step solutions to the classic 'fill in the table' exam questions.
Hashing is the way to answer “is this key present?” in constant time on average. A BST does this in , a hash table in . Java’s HashMap and HashSet classes are built on it. A compiler’s symbol table, caches and database indexes all use hashing.
The question that comes up most often from this week in the exam: “Insert these keys into this table with this method, write the final table.” Whoever applies the method correctly gets full marks. Golden rules:
- The home cell is . Compute it for every key first and write it aside.
- If there is a collision, look at the next cell according to the method’s formula. In the formula you always start from the home cell, not from the last cell tried.
- Everything is mod M. You wrap around from the end of the table to the beginning.
- In open addressing, deletion is lazy.
- When the λ threshold is passed, rehash: a new size, new home cells.
Basic concepts
Hash function: converts a key to an index between 0 and M-1. A good hash function is fast to compute and spreads the keys evenly over the table. For integer keys the standard choice is with M prime.
Why prime? If you choose M = 10, only the last digit matters; if most keys end in 0, they all fall into the same cell. A prime M mixes all the digits of the key.
For Strings Java uses this formula (computed with Horner’s method):
System.out.println("ab".hashCode()); // 97*31 + 98
System.out.println("cat".hashCode());
System.out.println("act".hashCode());
3105
98262
96402
If you only added the character codes, “cat” and “act” would both give 312. Multiplying by 31 takes the order of the letters into account.
PITFALL · negative hashCode. For long Strings the sum does not fit in an int and overflows, so
hashCode()can be negative.h % Mis then negative too and the array index blows up. EvenMath.abs(h) % Mis not safe:Math.abs(Integer.MIN_VALUE)is still negative. The correct way isMath.floorMod(h, M).
Collision: two different keys fall into the same cell. Since M is smaller than the number of possible keys, collisions are unavoidable. The question is how you resolve them.
Load factor: . This is the single number that determines performance.
We will use the same keys in all examples on this page:
M = 11
key 27 18 29 28 39 13 16
k mod 11 5 7 7 6 6 2 5
Separate chaining
Each cell holds a linked list. Keys that fall into the same cell are added to that list. We add the new key to the front of the list ().
0: -
1: -
2: 13
3: -
4: -
5: 16 -> 27
6: 39 -> 28
7: 29 -> 18
8: -
9: -
10: -
Search: go to the home cell and scan that list. Since the average list length is λ, an unsuccessful search examines λ elements on average, and a successful search about . In chaining, λ around 1 is reasonable; λ > 1 also works but gets slower. Deletion is easy: remove it from the list.
Open addressing
There are no lists; the keys sit directly in the array. On a collision, an empty cell is searched with a probe sequence: Here is required, and in practice is the target.
Linear probing
If it is full, look one to the right. The trace of the example table (in parentheses: the number of cells examined):
27 -> 5 (1)
18 -> 7 (1)
29 -> 7x 8 (2)
28 -> 6 (1)
39 -> 6x 7x 8x 9 (4)
13 -> 2 (1)
16 -> 5x 6x 7x 8x 9x 10 (6)
idx: 0 1 2 3 4 5 6 7 8 9 10
key: - - 13 - - 27 28 18 29 39 16
A single full block formed from 5 to 10. Every new key that falls into the block is added at its end and the block grows further. This is called primary clustering. It is the biggest weakness of linear probing: the home cell of 16 was 5 but it visited 6 cells.
Expected probe counts:
| λ | Successful search | Unsuccessful |
|---|---|---|
| 0.50 | 1.50 | 2.50 |
| 0.75 | 2.50 | 8.50 |
| 0.90 | 5.50 | 50.50 |
When λ rises to 0.9, an unsuccessful search visits 50 cells on average. That is why the rehash threshold exists.
Quadratic probing
In order: home + 0, +1, +4, +9, +16, … It moves away from the cluster quickly.
27 -> 5
18 -> 7
29 -> 7x 8 (7+1)
28 -> 6
39 -> 6x 7x 10 (6+1, 6+4)
13 -> 2
16 -> 5x 6x 9 (5+1, 5+4)
idx: 0 1 2 3 4 5 6 7 8 9 10
key: - - 13 - - 27 28 18 29 16 39
This time 16 was placed in 3 probes. Primary clustering goes away, but keys that fall into the same home cell still follow the same path: secondary clustering.
PITFALL · in quadratic probing, i² is added to the home cell. For 39, the second attempt is 7, and the third attempt is not 7 + 4 = 11 but 6 + 4 = 10. Each attempt is ; it is not added to the previous attempt.
Why M prime and λ ≤ 0.5? If M is not prime, takes few different values. For M = 16, the cells reachable from home cell 0 are only {0, 1, 4, 9}. Even when the table is half empty, a place may not be found. If M is prime, the first attempts always fall in different cells; if the table is at least half empty, an empty cell is certainly reached.
Double hashing
R is a prime smaller than M. Since the step size depends on the key, two keys that fall into the same home cell follow different paths; secondary clustering goes away too. must never be 0 (otherwise it stays in place); is between 1 and R, so it is safe.
With R = 7:
key h h2 path
27 5 1 5
18 7 3 7
29 7 6 7x 2 (7+6=13, mod 11 = 2)
28 6 7 6
39 6 3 6x 9
13 2 1 2x 3 (29 is at 2)
16 5 5 5x 10
idx: 0 1 2 3 4 5 6 7 8 9 10
key: - - 29 13 - 27 28 18 - 39 16
Note: 29 being placed at 2 pushed 13, whose home cell is 2, aside later. Do not mix up the order; insert the keys in the given order.
Deletion: lazy deletion
In the linear table we want to delete 29 (index 8). If we empty the cell, searching for 39 breaks: the path of 39 is 6 → 7 → 8 → 9. The search sees an empty cell at 8, says “39 is absent” and stops. Yet 39 is at index 9.
The solution: put a DELETED mark on the cell.
idx: 0 1 2 3 4 5 6 7 8 9 10
key: - - 13 - - 27 28 18 D 39 16
- Search passes over DELETED as if it were full: for 39, 6, 7, 8, 9 → 4 cells, found.
- Insertion can use DELETED as if it were empty: for 50 (h = 6), 6 and 7 are full, 8 is DELETED → 50 is written there.
If there are many deletions, DELETED cells pile up and searches get longer; rehashing cleans these up too. This problem does not exist in chaining; the node is just removed from the list.
Rehashing
When the λ threshold is passed, a larger table is opened (usually the first prime above 2M) and every key is hashed again with the new M. Copying to the old indexes is wrong, because has changed.
M = 7, linear probing, rehash if λ > 0.5 after an insertion:
insert 10 λ=0.143 3:10
insert 3 λ=0.286 3:10 4:3
insert 17 λ=0.429 3:10 4:3 5:17
insert 24 λ=0.571 3:10 4:3 5:17 6:24 -> rehash
new M = first prime >= 14 = 17
10 -> 10 3 -> 3 17 -> 0 24 -> 7
0:17 3:3 7:24 10:10
In the old table the four keys had formed a cluster between 3 and 6; in the new table there is no collision at all. Rehashing takes but happens rarely. Since the size doubles each time, the amortized cost per insertion stays . Java’s HashMap doubles its capacity by default when λ > 0.75.
Which method when?
| Method | Clustering | λ limit | Deletion | Note |
|---|---|---|---|---|
| Separate chaining | none | can be > 1 | easy | extra memory: list nodes |
| Linear | primary | ≤ 0.5 recommended | lazy | cache friendly, simple |
| Quadratic | secondary | ≤ 0.5, M prime | lazy | guarantee of space is conditional |
| Double hashing | almost none | ≤ 0.5 recommended | lazy | two hash computations |
A hash table does not know order. For jobs like “print the keys in sorted order”, “the smallest key”, “the keys between 10 and 20”, choose a BST or AVL.
Common mistakes
| Wrong idea | The truth |
|---|---|
| In quadratic probing, add to the previous one at each step | Each attempt is added to the home cell: |
| In double hashing, | It can be 0; use |
| In open addressing, deletion = empty the cell | A DELETED mark; otherwise searches break |
| In rehash, keys are copied to their old indexes | All are hashed again with the new M |
| In chaining, a new key is added at the end | Whatever the question says; usually at the front, |
| Probing stops at the end of the table | It wraps around to the start with mod M |
Math.abs(h) % M is always safe | Negative for Integer.MIN_VALUE; use floorMod |
| Finding the min in a hash table is | ; a hash does not keep order |
Test yourself
Exam-style questions. Solve them on paper first, then reveal the answer.
A hash table of size 11 holds 7 keys. What is its load factor ? For which collision strategy may exceed 1?
Show answer
. In open addressing there is at most one key per cell, so is required. In separate chaining the lists can grow longer, so is possible (but performance drops).
Insert 27, 18, 29, 28, 39, 13, 16 into a table of size 11 with using separate chaining (new keys go to the front of the list). Give all non-empty chains.
Show answer
Hash values: 27→5, 18→7, 29→7, 28→6, 39→6, 13→2, 16→5.
Since insertion is at the front, the later key stays in front: at index 5, first 27, then 16 goes to the front → [16, 27]. Likewise 6: [39, 28], 7: [29, 18].
Insert 27, 18, 29, 28, 39, 13, 16 into an empty table of size 11 using linear probing with . Give the final table (index:key).
Show answer
27 → 5. 18 → 7. 29 → 7 is full, 8.
28 → 6. 39 → 6 full, 7 full, 8 full, 9 empty (4 probes).
13 → 2. 16 → 5, 6, 7, 8, 9 full, 10 empty (6 probes).
A single cluster formed between 5 and 10: primary clustering.
Same keys and table size, but with quadratic probing (). Give the final table.
Show answer
The first four keys are the same as with linear probing: 27→5, 18→7, 29→7+1=8, 28→6.
39: h=6 full, 6+1=7 full, 6+4=10 empty → 10.
13 → 2.
16: h=5 full, 5+1=6 full, 5+4=9 empty → 9.
Same keys, size 11, double hashing with and probe sequence . Give the final table.
Show answer
27 → 5. 18 → 7.
29: h=7 full, , (7+6) mod 11 = 2 → 2.
28 → 6.
39: h=6 full, , 6+3 = 9 → 9.
13: h=2 full (29), , 3 → 3.
16: h=5 full, , 10 → 10.
In the linear probing table above (2:13 5:27 6:28 7:18 8:29 9:39 10:16), how many cells are examined by an unsuccessful search for key 49?
Show answer
49 mod 11 = 5. The search looks at 5, 6, 7, 8, 9, 10: six full cells, none of them is 49. Then it wraps around with mod 11 to 0: an empty cell, and the search ends. The empty cell also counts as an examined cell: 6 + 1 = 7.
What is printed?
System.out.println("ab".hashCode());
System.out.println("cat".hashCode() == "act".hashCode());Show answer
String.hashCode: . "ab" = 97·31 + 98 = 3105. "cat" = 98262, "act" = 96402: different. If you just added the characters, both would be 312; multiplying by 31 takes the order into account.
Using linear probing on the table 2:13 5:27 6:28 7:18 8:29 9:39 10:16, key 29 is deleted. Why must the cell be marked DELETED instead of emptied? How many cells does a search for 39 examine after a lazy delete?
Show answer
The probe path of 39 is 6 → 7 → 8 → 9. If cell 8 is really emptied, the search sees "empty" at 8 and stops, and 39 is thought to be absent (3 probes, wrong result).
In lazy deletion a DELETED mark is put on 8; the search passes over it: 6, 7, 8, 9 → 4 cells, 39 is found.
An insertion can reuse the DELETED cell: when 50 (h=6) is inserted, 6 and 7 are full, 8 is DELETED → 50 is written there.
A table of size 7 uses linear probing and is rehashed when after an insertion; the new size is the first prime . Insert 10, 3, 17, 24. Give the final table (old table scanned from index 0 when reinserting).
Show answer
M=7: 10→3, 3→3 full→4, 17→3,4 full→5 (λ=3/7≈0.43), 24→3,4,5 full→6. λ = 4/7 ≈ 0.57 > 0.5 → rehash.
The new size: 2·7 = 14 → the first prime is 17.
The old table is scanned from 0: 10 (index 3) → 10 mod 17 = 10; 3 → 3; 17 → 0; 24 → 7.
The clustering disappeared, there is no collision at all.
Why should the table size be prime when using quadratic probing? (Hint: list the cells reachable from home cell 0 when M = 16.)
Show answer
For M = 16, takes only the values {0, 1, 4, 9}: even when the table is half empty, a free place may not be found. If M is prime, the first probes always fall in different cells (for M=11, {0,1,3,4,5,9}). So the rule: if M is prime and , insertion always succeeds.
Write a method int insert(int[] table, int key) for a linear-probing table of non-negative ints where -1 marks an empty cell. Return the index where the key was stored, or -1 if the table is full.
Show answer
Start from the home cell and move forward with wraparound, increasing i.
static int insert(int[] table, int key) {
int M = table.length;
int h = key % M;
for (int i = 0; i < M; i++) {
int p = (h + i) % M;
if (table[p] == -1) {
table[p] = key;
return p;
}
}
return -1; // table is full
}If there are negative keys or hashCodes, key % M can be negative; then use Math.floorMod(key, M).
Give the average and worst-case time of search in a hash table with a good hash function and constant load factor. When does the worst case happen?
Show answer
When λ is kept constant (with rehashing), the expected number of probes is constant: . If a bad hash function sends all keys to the same cell, a single list forms in chaining and a single cluster in open addressing, and search is . A hash table is not suitable for sorted traversal or min/max.
Verification: All tables, probe counts, the rehash result, String.hashCode values and the expected-probe formulas were computed with Java (verify/SEN2212/W09.java, java W09.java).