Why is insertion into a growth-doubling hash table amortized O(1) when one insert rehashes everything?
answer
- count the resizes, not the inserts
- each resize moves the entries present then
- how does 16, 32, 64 ... sum
- the last resize is half the total work
- worst-case sequence, not an input distribution
basics
~20 sBecause doubling makes resizes exponentially rarer, so the total re-bucketing work across n inserts stays under about 2n and averages to a constant per insert over the whole sequence. It never promises that any individual insert is cheap.
solid answer
~50 sGrowth by a constant factor means the table resizes at capacities 16, 32, 64 and so on, and each resize moves the entries present at that moment. Those move counts form a geometric series dominated by the last term, so building a table of one million entries costs roughly 1.5 million total entry moves — under two extra moves per insert averaged over the sequence. That is what "amortized O(1)" asserts: a bound on the *total* cost of a worst-case sequence divided by its length. It is not average-case analysis, which would assume a distribution over inputs; and it explicitly does not say every insert is fast. In that same million-entry table, one particular insert moved 786,432 entries before returning. If your latency budget is per-operation rather than per-batch, the amortized bound is the wrong bound to quote.
code
pseudocode · 8 lines// C = capacity, n = entry count, L = max load factor
insert(k, v)
if (n + 1) > L * C
C = 2 * C
rehash all n entries into the new bucket array // O(n)
j = hash(k) mod C
add (k, v) to bucket[j] // O(1)
n = n + 1go deeper
Know the headline: doubling makes resizes rarer and rarer, so the copying work spread over all the inserts averages out to a constant. Recall that the resizing insert itself is the expensive one.
Do the arithmetic out loud — resize points, entries moved at each, the geometric sum — and then draw the line between an amortized bound over a sequence and an average over a distribution.
Show where the bound stops being useful: per-operation SLOs, a lock held across the transfer, and the transient double allocation. Name collisions as a second, independent reason insert is not flatly O(1).
Be ready to argue when an amortized bound is the right contract to promise a caller at all, and when the team should be quoting a tail number instead because the workload is latency-shaped rather than throughput-shaped.
## The claim, stated precisely Insertion into a hash table that grows by a constant factor is **amortized O(1)**: any sequence of n insertions costs O(n) total, so the cost per insertion averaged over the sequence is bounded by a constant. Two words in that sentence do the heavy lifting — *sequence* and *total*. ## Why the total stays linear Suppose the table starts with 16 buckets and doubles whenever the entry count would exceed 0.75 of capacity. Building up to one million distinct keys, it resizes at capacities 16, 32, 64, … , 1,048,576 — seventeen resizes. The number of entries moved at each resize is the entry count at that moment, roughly 0.75 times the capacity being outgrown. Summing: 0.75 x (16 + 32 + … + 1,048,576) ≈ 0.75 x 2,097,136 ≈ **1.57 million entry moves** for one million insertions. The series is geometric, so it sums to a small constant multiple of its largest term — the final resize alone accounts for half the work, and everything before it accounts for the other half combined. Add 1.57 million moves to 1 million O(1) placements and the whole build is Θ(n). Divide by n and you have your constant. The constant factor of growth is load-bearing. If the table instead grew by a **fixed** number of buckets, it would resize Θ(n) times while each resize moved Θ(n) entries, and the build would degrade to Θ(n²) — the same million keys would cost on the order of a trillion moves. Growth *rate*, not growth *amount*, is what makes the series converge. ## Amortized is not average-case This is the single most common misstatement in the whole subject. - **Amortized** is a worst-case statement about a sequence. No probability is involved. Whatever adversarial order you choose, n inserts cost O(n) in re-bucketing work; the bound holds every time you run it. - **Average-case** assumes a probability distribution over inputs and reports an expectation. Change the distribution and the number changes. They happen to coexist here, which is why the confusion survives. Hash-table insertion has *two independent* reasons not to be flatly O(1): 1. **Resizing**, which is amortized away by doubling — a deterministic, distribution-free argument. 2. **Collisions**, which are handled by the *expected* O(1) argument and depend on the hash spreading the actual key set well. Against an adversary who chooses keys that collide, a bucket degenerates into a linear scan and a single lookup or insert becomes O(n) — no amount of resizing fixes that, which is why serious implementations randomise their hashing or use collision-resistant treeing. A candidate who says "insert is O(1) amortized" and stops has answered only half. The honest phrasing is "O(1) expected and amortized; Θ(n) for the insert that triggers a resize, and Θ(n) per operation under adversarial collisions." ## What the bound does not promise Amortized O(1) says nothing about the distribution of cost *within* the sequence, and the distribution here is brutally lopsided. In the million-key example, one insert — the one that crossed 786,432 entries — moved 786,432 entries before it returned. Roughly 999,983 of the inserts touched a single bucket; seventeen of them did essentially all the work. That matters for three real situations: - **Per-operation latency budgets.** A p99 or p999 SLO is a statement about individual operations. The amortized bound is silent about the tail, and the tail is precisely where the resizes live. - **Holding a lock.** If the table is guarded, the resizing thread holds the lock for the whole transfer, so the pause is charged to every waiting caller, not just the unlucky writer. - **Memory spikes.** During the transfer both arrays are live. The amortized *time* bound says nothing about that transient allocation peak, which can be what actually breaks a memory-constrained process. ## How to answer it out loud Run the numbers, then state the limit. "Each resize moves the entries present; doubling makes those move counts a geometric series summing to under twice the final size, so total work over n inserts is linear and per-insert amortized cost is constant. What that does *not* say is that any given insert is constant — the one that crosses the threshold moves every entry in the table, and if I have a tail-latency budget I have to plan for that separately." The second sentence is the one that distinguishes a middle candidate from a junior reciting a phrase.
- How would the analysis change if the table grew by a fixed 1,000 buckets each time instead of doubling?It would collapse. Reaching n entries would take Θ(n) resizes, each moving Θ(n) entries, so total work becomes Θ(n²) and per-insert amortized cost becomes Θ(n). The geometric series only converges because the growth is multiplicative; with additive growth the resize counts and the move counts both scale with n.
- Is the amortized bound still valid if the same sequence includes deletions?For growth, yes — deletions only reduce the entry count. The bound breaks if the table also shrinks at the same threshold it grows at, because a workload sitting on the boundary can be made to resize on every other operation. Implementations that shrink use hysteresis: they grow at one load factor and shrink at a distinctly lower one so the two thresholds cannot ping-pong.
- A teammate says "amortized O(1) means it's O(1) on average". What do you correct?Average-case analysis assumes a probability distribution over inputs and reports an expectation; amortized analysis assumes nothing and bounds the total cost of a worst-case sequence. The distinction is practical: an adversary can defeat an average-case claim by choosing bad inputs, but cannot defeat the amortized resizing bound. Hash tables happen to need both claims — amortized for resizing, expected for collisions.
saying these in an interview costs you the question
- Says amortized means average-case
- Claims every individual insert is therefore fast
- Cannot explain why the total work is linear
- Forgets collisions are a separate reason inserts miss O(1)
- Thinks growing by a fixed number of buckets works just as well