Definition · Hashing with chainingA table of m slots and a hash function h from keys to {0, …, m−1}. Each slot holds a linked list — a chain — of every key that hashed to it. Insert prepends to the chain; search computes h(k) and walks the chain; delete unlinks. The load factor is α = n/m, the number of keys divided by the number of slots.
The division hash function is h(k) = k mod m. It is the one this lesson uses, because its failure mode is arithmetic rather than mysterious.
Theorem · Expected search cost under simple uniform hashingAssume each key is equally likely to hash to any of the m slots, independently. Then an unsuccessful search examines α keys in expectation, and a successful search examines 1 + α/2 − α/2m. Both are Θ(1 + α) once the constant probe for the slot itself is counted.
The unsuccessful case is immediate: the search walks the whole chain at h(k), whose expected length is n/m = α by linearity of expectation over the n keys. The successful case is smaller than α because the search stops at the key it wants and, on average, that key is halfway along — and the correction −α/2m is because the key being searched for is itself one of the n, so it does not collide with itself.
Linearity of expectation is doing all the work in the first half, and it is imported from Discrete Mathematics’ “Linearity of Expectation”: the expected number of keys in a given slot is the sum over keys of the probability that key lands there, which is n × 1/m. No independence is needed for that step, only for the assumption to be stated the way it usually is.
The fractions, at a table small enough to draw
Twenty keys into eight slots. The lab computes every quantity as an exact fraction over big integers, never as a decimal that is nearly one, and then counts the probes an actual search makes:
m = 8, n = 20 α = 20/8 = 5/2 = 2.5
expected unsuccessful α = 5/2 = 2.5
expected successful 1 + α/2 − α/2m
= 1 + 5/4 − 5/32 = 67/32 = 2.09375
measured, twenty keys drawn from the stream:
chains 4 5 1 2 0 1 4 3 longest 5, one empty
mean probes over the twenty searches 23/10 = 2.3
23/10 against an expected 67/32: the measurement came in above the expectation, which is ordinary at twenty keys, and the two numbers are close. The chain lengths 4, 5, 1, 2, 0, 1, 4, 3 add to twenty and average 2.5, as they must — the average chain length is α by arithmetic, not by luck. What varies is how unevenly the keys landed, and the longest chain here is 5, twice the average.
The same load factor, and a table that has become a list
Now keep m = 8 and n = 20 and choose the keys to be 8, 16, 24, …, 160 — every one a multiple of 8. Each of them hashes to slot 0:
m = 8, n = 20, keys 8 16 24 … 160 α = 5/2, unchanged
chains 20 0 0 0 0 0 0 0 longest 20, seven empty
mean probes over the twenty searches 21/2 = 10.5
same α, expected 67/32 ≈ 2.09, measured 10.5 a factor of 5
The load factor did not move. The expectation did not move. The measured cost went up by a factor of five, and it is not a fluke or a bad seed — it is the worst case, reached deliberately, by keys that a real application produces all the time: record identifiers spaced by a round number, addresses aligned to a power of two, prices in whole units. The table is now a linked list with an index on the front of it.
So the quotable statement about a hash lookup has two halves and needs both: Θ(1 + α) expected under simple uniform hashing, Θ(n) in the worst case, for any fixed hash function. System Design’s “Hash vs Tree for Ranges” is where those same two facts decide a disk index rather than an in-memory one, and there the second half is joined by a third problem: a hash index cannot answer a range query at all, at any load factor.