Learn
Lesson · Data Structures · Hash tables

Resizing and the Load Factor

A hash table keeps its load factor in range by doubling when it fills and halving when it empties, and the two thresholds must be different. Doubling at α = 1 and halving at α = ¼ costs 52 over forty operations at the boundary; doubling and halving both at α = ½ costs 495 for the same forty, rehashing on thirty-nine of them. The gap between the thresholds is the whole mechanism.

grow α ≥ 1 double m, rehash every key shrink α ≤ 1/4 halve m, rehash every key the two thresholds must differ, and by a factor of more than 2 40 operations alternating at the boundary, from m = 4: grow at 1, shrink at 1/4 2 rehashes, 12 keys moved, total 52 grow at 1/2, shrink at 1/2 39 rehashes, 455 keys moved, total 495
Two thresholds, and what happens when they are the same one

Core ideas

What this lesson establishes

One idea with a name worth learning — hysteresis — and two consequences: why the amortised bound holds when the thresholds differ, and why it fails completely when they do not.

1

Rehashing is unavoidable and that is fine

Every key’s slot is computed from m, so changing m means recomputing every slot: a resize is Θ(n) and there is no cleverer way. The bound is amortised, exactly as for the two-stack queue: an operation that triggers a resize is expensive, and the sequence containing it is not, because doubling makes resizes exponentially rare.

2

The two thresholds must leave a gap

If the table grows at α = ½ and shrinks at α = ½, then one insertion past the boundary doubles the table, which halves α, which triggers a shrink, which doubles α, and the next operation resizes again. The gap — grow at 1, shrink at ¼ — means that after any resize the load is far from both thresholds, so many operations must happen before another one is due. That gap is called hysteresis.

3

The amortised bound is a claim about every sequence, and the lab can break it

3 per operation is the promise: forty operations should cost at most 120. The hysteresis policy costs 52, comfortably inside. The equal thresholds cost 495, four times the bound, on the same forty operations — which is what it looks like when a policy has no amortised bound at all rather than a loose one.

The material

Two thresholds, the potential argument, and the policy that thrashes

Why doubling gives an amortised constant, why the shrink threshold must be lower, and the two policies counted on the same sequence.

A table’s cost depends on its load factor, so the load factor has to be kept in a band. Too high and chains grow or probes cluster; too low and most of the memory is empty. The mechanism is to pick thresholds and resize when one is crossed, rehashing every key into the new table.

Definition · Growth and shrink thresholds

A table grows — usually doubling m — when α reaches the growth threshold, and shrinks — usually halving m — when α falls to the shrink threshold. Both resizes rehash all n keys and cost Θ(n).

The standard pair is grow at α = 1 and shrink at α = ¼. The shrink threshold is deliberately far below half the growth threshold, and the next paragraph is why.

Theorem · Doubling gives an amortised constant cost

Under a policy that doubles at α = 1 and halves at α = ¼, a sequence of k insertions and deletions on a table starting empty costs O(k) in total, so O(1) amortised per operation.

The potential method: give the table a potential that measures how close it is to its next resize — roughly 2n − m above half full and m/2 − n below. An ordinary operation changes n by one and so changes the potential by a constant. A resize costs n and releases potential that the operations since the last resize have paid in, because the gap between the thresholds guarantees there were at least about n/2 of them. The released potential covers the rehash, and the amortised cost of every operation is constant.

That proof needs the gap. If growth and shrink happen at the same load, then immediately after a resize the table is sitting on the other threshold, no operations have accumulated any potential, and the next operation resizes again. There is nothing to release and the argument has no step to make. The failure is not a worse constant; it is the absence of a bound.

Both policies, on the same forty operations

The lab’s boundary pattern fills a table and then alternates inserting and deleting, which is the mix designed to sit on a threshold. Forty operations from m = 4:

grow at α = 1, shrink at α = 1/4 rehashes 2 at ops 4 (4→8) and 8 (8→16) keys moved 12 total cost 52 against the 3m line at 120 amortised per op 13/10 = 1.3 final table m = 16 holding 14 keys no thrash detected grow at α = 1/2, shrink at α = 1/2 rehashes 39 of 40 operations keys moved 455 total cost 495 against the 3m line at 120 amortised per op 99/8 = 12.375 thrashing detected from operation 2

Read the two rehash counts first. Two resizes in forty operations against thirty-nine. The hysteresis policy resizes twice, early, while the table is growing into its final size, and then never again — the alternating insertions and deletions move α around inside the band and never reach an edge of it. The equal-threshold policy resizes on almost every operation, oscillating between m = 16 and m = 32 from operation 8 onward and moving thirteen or fourteen keys on every resize from operation 13.

The total cost is 52 against 495, and the second one has broken the bound: 3 per operation would be 120. The measured amortised figures are 1.3 and 12.375, and the second is not a constant — it grows with the table, because each thrashing resize costs n and n is proportional to m. Doubling the sequence length on the thrashing policy roughly quadruples its cost.

Example · Growth alone, for the shape of the amortised line

Forty insertions and no deletions, same thresholds: four rehashes at m = 4, 8, 16, 32, moving 4 + 8 + 16 + 32 = 60 keys in total, for a cost of 100 against the 120 line. The rehash cost of a table that reaches size n is about 2n whatever the sequence, because the doubling costs form a geometric series — which is the cleanest case of the amortised argument and the one to look at before the boundary pattern.

One caution about the measured amortised figure. 13/10 is the total divided by the number of operations for this sequence, and it is below 3, and that is not the bound holding — it is one sequence coming in under it. A different pattern at the same thresholds could come closer to 3 and none can exceed it, and the reason none can is the potential argument above rather than anything the lab did. The lab is what shows you the thrashing policy, which no amount of reasoning about the good policy would have revealed.

Interactive

Resizing, and why the two thresholds differ

Double at one load, halve at a lower one, or every operation rehashes

Growing, shrinking and the gap betweenkeys moved, counted; the amortised cost, exact
cost so farthe 3m linetable sizethrashing

Method

Choosing and testing a resize policy

Pick the thresholds, then attack them with the pattern designed to sit between them. A policy tested only on growth has not been tested.

1

State both thresholds, and check the gap

Grow at α_g, shrink at α_s, and require α_s to be well below α_g/2. 1 and ¼ is the standard pair. Equal thresholds, or ½ and ¼, are the ones to be suspicious of, and the reason is arithmetic: after a resize the new load must be far from both edges.

2

Run the monotone pattern first

Insertions only. The rehash costs form a geometric series, the total is about 2n, and the cumulative line should sit comfortably under the 3m line. If it does not, the policy is wrong in the easy case and there is no point going further.

3

Now alternate at the boundary

Fill the table to just under the growth threshold, then insert and delete alternately. This is the pattern a bad policy dies on, and it is not exotic — a cache or a work queue whose size hovers around a threshold produces it naturally.

4

Count rehashes, not just total cost

The rehash count is the diagnostic; the cost is the symptom. Two rehashes in forty operations is a healthy policy. Thirty-nine in forty is a policy with no amortised bound, and the count says so immediately while the total needs comparing with something.

Thresholds and amortised cost

Score 0 / 0

Both policies on the boundary pattern, operation by operation

The first thirteen operations of the lab’s forty, under each policy, starting from m = 4. The pattern fills to thirteen keys and then alternates.

grow at 1, shrink at 1/4 grow at 1/2, shrink at 1/2 α is the load the operation produced, BEFORE any resize it triggered op n m α rehash n m α rehash 1 1 4 1/4 — 1 4 1/4 — 2 2 4 1/2 — 2 8 1/2 4 → 8, 2 keys 3 3 4 3/4 — 3 4 3/8 8 → 4, 3 keys 4 4 8 1 4 → 8, 4 keys 4 8 1 4 → 8, 4 keys 5 5 8 5/8 — 5 16 5/8 8 → 16, 5 keys 6 6 8 3/4 — 6 8 3/8 16 → 8, 6 keys 7 7 8 7/8 — 7 16 7/8 8 → 16, 7 keys 8 8 16 1 8 → 16, 8 keys 8 32 1/2 16 → 32, 8 keys 9 9 16 9/16 — 9 16 9/32 32 → 16, 9 keys 10 10 16 5/8 — 10 32 5/8 16 → 32, 10 keys 11 11 16 11/16 — 11 16 11/32 32 → 16, 11 keys 12 12 16 3/4 — 12 32 3/4 16 → 32, 12 keys 13 13 16 13/16 — 13 16 13/32 32 → 16, 13 keys after 40 operations: 2 rehashes, 12 moved, total 52 39 rehashes, 455 moved, total 495 amortised 13/10 amortised 99/8 3m line 120: inside 3m line 120: broken, 4× over

The left column stops resizing after operation 8 and never starts again: α wanders between 9/16 and 7/8 for the remaining thirty-two operations, and neither edge of the band is anywhere near. That is hysteresis working, and it is the whole of the mechanism — there is no cleverness in it beyond choosing two numbers far apart.

The right column resizes on twelve of the first thirteen operations, and keeps going for the rest. Look at operation 8: the insertion pushes α to 1/2, which is the growth threshold, so the table doubles to 32 and α becomes 1/4 — which is below the shrink threshold of 1/2, so the next operation halves it back. The policy has trapped itself between its own two rules.

For a faded rehearsal, try grow at 1 and shrink at 1/2 — a gap, but only a factor of two. The supplied observation is that doubling at α = 1 leaves α = 1/2, which is exactly the shrink threshold. Predict what the operation after a growth does, then run it, and say how far apart the thresholds have to be for the potential argument to have anything to release.

Common mistakes

1
Shrinking at half the growth threshold, or at the same one“Grow when full, shrink when half empty” sounds symmetric and thrashes: doubling at α = 1 leaves α = 1/2, which is the shrink threshold, so the next deletion halves the table back. The standard 1 and ¼ is not arbitrary — the gap is what the potential argument spends, and without it there is no bound to state.
2
Reading the measured average as the amortised constant52 over forty operations is 1.3, and 495 over the same forty is 12.375. The first is a sequence that came in under a bound proved elsewhere; the second is a policy with no bound, measured on one sequence that happened to expose it. An average over a run is a number about that run, and the only thing that makes it a guarantee is an argument.
3
Testing a resize policy on growth onlyMonotone insertion is the easy case: the geometric series of doubling costs keeps the total near 2n under almost any threshold. Every policy looks fine there. The pattern that distinguishes them is alternation at the boundary, and a cache or queue whose size hovers near a threshold produces it without anyone constructing it.

Check your understanding

✓
Finish when “shrink when half empty” sounds like a bug report.

You should be able to state a growth and a shrink threshold and say what the load factor becomes immediately after each kind of resize, count rehashes and keys moved over a stated sequence, recognise thrashing from the rehash count rather than the total, and separate a measured average from the amortised bound the potential argument proves.

Note: Hash tables answer membership and refuse order: no predecessor, no range, no sorted walk, at any load factor. The next five lessons are about the structure that keeps order and pays for it in constants. “Binary Search Trees” starts with the invariant — and with the fact that it is not a check on a node and its children, which the lab settles with a tree that passes the local check and is not a search tree.

Kept in this browser only, for you. Nothing is sent anywhere and nothing checks it. Carry it to another device.

Feedback and recommendations

Something to change about this lesson? Write it down, tick it off when it is handled, and export the list when you want to act on it. Kept in this browser only: nothing is sent anywhere, and nobody is notified.

No recommendations yet for this lesson.