SEN2212 · Week 9 · Analyzing and implementing hashing algorithms.

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.

hash tablehash functioncollisionload factorseparate chainingopen addressinglinear probingprimary clusteringquadratic probingsecondary clusteringdouble hashinglazy deletionrehashinghashCode

Hashing is the way to answer “is this key present?” in constant time on average. A BST does this in O(log⁡n)O(\log n), a hash table in O(1)O(1). 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:

  1. The home cell is h(k)=k mod Mh(k) = k \bmod M. Compute it for every key first and write it aside.
  2. 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.
  3. Everything is mod M. You wrap around from the end of the table to the beginning.
  4. In open addressing, deletion is lazy.
  5. 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 k mod Mk \bmod M 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):

h(s)=s0⋅31n−1+s1⋅31n−2+⋯+sn−1h(s) = s_0 \cdot 31^{n-1} + s_1 \cdot 31^{n-2} + \dots + s_{n-1}

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 % M is then negative too and the array index blows up. Even Math.abs(h) % M is not safe: Math.abs(Integer.MIN_VALUE) is still negative. The correct way is Math.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: λ=n/M\lambda = n / M. 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 (O(1)O(1)).

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 1+λ/21 + \lambda/2. 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: h0,h1,h2,…h_0, h_1, h_2, \dots Here λ≤1\lambda \le 1 is required, and in practice λ≤0.5\lambda \le 0.5 is the target.

Linear probing

hi(k)=(h(k)+i) mod Mh_i(k) = (h(k) + i) \bmod M

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 12(1+11−λ)\tfrac12(1+\tfrac{1}{1-\lambda})Unsuccessful 12(1+1(1−λ)2)\tfrac12(1+\tfrac{1}{(1-\lambda)^2})
0.501.502.50
0.752.508.50
0.905.5050.50

When λ rises to 0.9, an unsuccessful search visits 50 cells on average. That is why the rehash threshold exists.

Quadratic probing

hi(k)=(h(k)+i2) mod Mh_i(k) = (h(k) + i^2) \bmod M

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 h(k)+i2h(k) + i^2; it is not added to the previous attempt.

Why M prime and λ ≤ 0.5? If M is not prime, i2 mod Mi^2 \bmod M 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 ⌈M/2⌉\lceil M/2 \rceil attempts always fall in different cells; if the table is at least half empty, an empty cell is certainly reached.

Double hashing

hi(k)=(h(k)+i⋅h2(k)) mod M,h2(k)=R−(k mod R)h_i(k) = (h(k) + i \cdot h_2(k)) \bmod M, \qquad h_2(k) = R - (k \bmod R)

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. h2h_2 must never be 0 (otherwise it stays in place); R−(k mod R)R - (k \bmod R) 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 k mod Mk \bmod M 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 O(n)O(n) but happens rarely. Since the size doubles each time, the amortized cost per insertion stays O(1)O(1). Java’s HashMap doubles its capacity by default when λ > 0.75.

Which method when?

MethodClusteringλ limitDeletionNote
Separate chainingnonecan be > 1easyextra memory: list nodes
Linearprimary≤ 0.5 recommendedlazycache friendly, simple
Quadraticsecondary≤ 0.5, M primelazyguarantee of space is conditional
Double hashingalmost none≤ 0.5 recommendedlazytwo 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 ideaThe truth
In quadratic probing, add i2i^2 to the previous one at each stepEach attempt is added to the home cell: h+i2h + i^2
In double hashing, h2=k mod Rh_2 = k \bmod RIt can be 0; use R−(k mod R)R - (k \bmod R)
In open addressing, deletion = empty the cellA DELETED mark; otherwise searches break
In rehash, keys are copied to their old indexesAll are hashed again with the new M
In chaining, a new key is added at the endWhatever the question says; usually at the front, O(1)O(1)
Probing stops at the end of the tableIt wraps around to the start with mod M
Math.abs(h) % M is always safeNegative for Integer.MIN_VALUE; use floorMod
Finding the min in a hash table is O(1)O(1)O(M)O(M); a hash does not keep order

Test yourself

Exam-style questions. Solve them on paper first, then reveal the answer.

Question 1easy

A hash table of size 11 holds 7 keys. What is its load factor λ\lambda? For which collision strategy may λ\lambda exceed 1?

Show answer
λ = 7/11 ≈ 0.64; separate chaining

λ=n/M=7/11≈0.636\lambda = n / M = 7/11 \approx 0.636. In open addressing there is at most one key per cell, so λ≤1\lambda \le 1 is required. In separate chaining the lists can grow longer, so λ>1\lambda > 1 is possible (but performance drops).

Question 2easy

Insert 27, 18, 29, 28, 39, 13, 16 into a table of size 11 with h(k)=k mod 11h(k) = k \bmod 11 using separate chaining (new keys go to the front of the list). Give all non-empty chains.

Show answer
2: [13]; 5: [16, 27]; 6: [39, 28]; 7: [29, 18]

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].

Question 3medium

Insert 27, 18, 29, 28, 39, 13, 16 into an empty table of size 11 using linear probing with h(k)=k mod 11h(k) = k \bmod 11. Give the final table (index:key).

Show answer
2:13 5:27 6:28 7:18 8:29 9:39 10:16

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.

Question 4medium

Same keys and table size, but with quadratic probing (h(k)+i2 mod 11h(k) + i^2 \bmod 11). Give the final table.

Show answer
2:13 5:27 6:28 7:18 8:29 9:16 10:39

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.

Question 5hard

Same keys, size 11, double hashing with h2(k)=7−(k mod 7)h_2(k) = 7 - (k \bmod 7) and probe sequence h(k)+i⋅h2(k) mod 11h(k) + i \cdot h_2(k) \bmod 11. Give the final table.

Show answer
2:29 3:13 5:27 6:28 7:18 9:39 10:16

27 → 5. 18 → 7.
29: h=7 full, h2=7−1=6h_2 = 7 - 1 = 6, (7+6) mod 11 = 2 → 2.
28 → 6.
39: h=6 full, h2=7−4=3h_2 = 7 - 4 = 3, 6+3 = 9 → 9.
13: h=2 full (29), h2=7−6=1h_2 = 7 - 6 = 1, 3 → 3.
16: h=5 full, h2=7−2=5h_2 = 7 - 2 = 5, 10 → 10.

Question 6medium

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
7

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.

Question 7medium

What is printed?

System.out.println("ab".hashCode());
System.out.println("cat".hashCode() == "act".hashCode());
Show answer
3105 and false

String.hashCode: s0⋅31n−1+⋯+sn−1s_0 \cdot 31^{n-1} + \dots + s_{n-1}. "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.

Question 8hard

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
Emptying breaks the probe chain; 4 cells

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.

Question 9hard

A table of size 7 uses linear probing and is rehashed when λ>0.5\lambda > 0.5 after an insertion; the new size is the first prime ≥2M\ge 2M. Insert 10, 3, 17, 24. Give the final table (old table scanned from index 0 when reinserting).

Show answer
size 17: 0:17 3:3 7:24 10:10

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.

Question 10medium

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
With M = 16 only cells 0, 1, 4, 9 are reachable; with prime M and λ ≤ 0.5 a free cell is guaranteed

For M = 16, i2 mod 16i^2 \bmod 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 ⌈M/2⌉\lceil M/2 \rceil probes always fall in different cells (for M=11, {0,1,3,4,5,9}). So the rule: if M is prime and λ≤0.5\lambda \le 0.5, insertion always succeeds.

Question 11medium

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
Probe (h + i) % M for i = 0..M-1; store at first -1

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).

Question 12easy

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
Average O(1), worst O(n); all keys collide

When λ is kept constant (with rehashing), the expected number of probes is constant: O(1)O(1). 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 O(n)O(n). 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).

Review card

h(k) = k mod M. λ = n/M. In open addressing λ ≤ 1, in chaining λ > 1 is possible. Chaining: each cell is a list. The average list length is λ. Insertion at the front is O(1). Linear: h, h+1, h+2, ... (mod M). Primary clustering. Aim for λ ≤ 0.5. Quadratic: h, h+1, h+4, h+9, ... A free place is guaranteed if M is PRIME and λ ≤ 0.5. Secondary clustering. Double hashing: h + i·h2(k), h2(k) = R - (k mod R), R < M prime. h2 must never be 0. If asked for the probe count: full cells + the last cell looked at (empty or found). Deletion in open addressing is LAZY: a DELETED mark. If you empty the cell the search chain breaks. Rehash: when λ passes the threshold, new M = first prime above 2M, reinsert all keys with the NEW M. O(n), amortized O(1). String.hashCode = s0·31^(n-1) + ... + s(n-1). "ab" = 3105. Can be negative: Math.floorMod. Math.abs(Integer.MIN_VALUE) is negative. -7 % 11 = -7, floorMod(-7, 11) = 4. Average O(1), worst O(n). For sorted output, min/max, range queries use a BST, NOT a hash. Expected probes (linear, unsuccessful): ½(1 + 1/(1-λ)²). λ=0.5 → 2.5, λ=0.9 → 50.5.