Amortized Analysis
Overview
Amortized analysis determines the average cost per operation over a worst-case sequence of operations, NOT the average case for a single random operation. It proves that even if a few individual operations are expensive (like resizing an array), the cost spread across all operations stays low. The three standard techniques are the aggregate method, the accounting method, and the potential method.
Worst-case per-operation analysis can be misleadingly pessimistic. If you judge a dynamic array's append by its worst case alone, you would conclude it is O(n) and avoid using it — even though a sequence of n appends costs only O(n) total, making each append O(1) amortized. Amortized analysis gives the true average cost per operation without relying on probabilistic assumptions.
Where used: Dynamic arrays (Python list, Java ArrayList, C++ vector): append is O(1) amortized because resizes are exponentially rare, Hash table rehashing: doubling the table and rehashing all keys is O(n), but it happens only every O(n) insertions, Splay trees and union-find with path compression: individual operations can be slow but any sequence of m operations completes in O(m log n) or O(m alpha(n)) total
Why learn this
- It prevents you from over-engineering solutions by incorrectly labeling efficient data structures as slow based on their worst-case per-operation cost.
- Senior-level interview discussions on hash tables, dynamic arrays, and self-balancing trees all require explaining amortized guarantees.
- It deepens your understanding of data structure design: the deliberate choice to make rare operations expensive in order to make common operations cheap.
Aha moment
# Dynamic array: append 8 elements, starting capacity 1
# Capacity: 1 -> 2 -> 4 -> 8 (3 resizes)
# Resize copies: 1 + 2 + 4 = 7 elements copied
# Non-resize appends: 8 - 3 = 5 (cost 1 each = 5)
# Total cost: 7 + 8 = 15 operations for 8 appends
# Amortized cost per append: 15/8 < 2
#
# For n appends: total resize copies = 1+2+4+...+n = 2n-1
# Total cost: (2n - 1) + n = 3n - 1 -> O(n) total -> O(1) each
Prediction: With 3 resizes that copy 1, 2, and 4 elements, the total cost must be high.
Common guess: The resizes make append more like O(n) overall.
Even though resizes copy 1+2+4 = 7 elements, there are 8 total appends, so the average cost is less than 2 operations per append. The crucial insight is that each element is copied at most O(log n) times across all resizes, and the geometric series ensures the total copy work is bounded by 2n. Expensive operations happen so rarely that they are absorbed by the many cheap ones.
Common mistakes
- Confusing amortized with average-case: Average-case analysis assumes a probability distribution over inputs — amortized analysis does not. Amortized cost is a worst-case guarantee over ANY sequence of operations. No randomness or assumptions are involved.
- Thinking amortized O(1) means every operation is fast: Amortized O(1) means the average per operation is O(1) when spread over many operations. A single operation CAN still take O(n) time (like a resize). In real-time systems where every single operation must meet a deadline, amortized guarantees are insufficient.
- Forgetting WHY doubling works but incrementing does not: If a dynamic array grows by a fixed amount k each time, a sequence of n appends triggers n/k resizes, each copying O(n) elements — giving O(n^2) total. Doubling ensures the geometric series 1 + 2 + 4 + ... + n sums to 2n, keeping the total at O(n). The exponential growth rate is essential.
Glossary
- probabilistic assumptions
- Relying on random chances instead of mathematical guarantees.
- geometric series
- A sequence of numbers where each term is found by multiplying the previous one by a fixed number.
Recall questions
- What is the key difference between amortized analysis and average-case analysis?
- A dynamic array doubles in capacity when full. What is the amortized cost of a single append and why?
- Name the three standard techniques for amortized analysis.
Understanding checks
A colleague proposes that instead of doubling, a dynamic array should add 100 extra slots each time it runs out of space, because 'we waste less memory.' Why does this destroy the amortized O(1) append guarantee?
With fixed-increment growth, after inserting n elements you trigger about n/100 resizes, and the i-th resize copies roughly 100*i elements. The total copy cost is proportional to 100 * (1 + 2 + ... + n/100) = O(n^2/100) = O(n^2). Dividing by n gives O(n) amortized cost per append, not O(1).
The geometric doubling strategy is not arbitrary — it ensures the total resize cost forms a geometric series that converges to O(n). Linear growth produces an arithmetic series that diverges to O(n^2). The growth factor must be multiplicative, not additive.
A developer avoids using Python lists for a real-time audio processing buffer because 'append is O(n) when it resizes, and audio stutters are unacceptable.' Is this reasoning valid or flawed?
The reasoning is VALID. In real-time systems, worst-case latency for every individual operation matters, not amortized averages. A single O(n) resize during audio playback causes a perceptible stutter. A pre-allocated ring buffer with true O(1) worst-case is the correct choice.
This shows the boundary of amortized analysis: it guarantees low AVERAGE cost but not low WORST-CASE cost for each individual operation. For latency-sensitive systems, amortized guarantees are not strong enough.
Practice tasks
Trace the resize sequence for 16 appends
A dynamic array starts at capacity 1 and doubles when full. Trace 16 appends: for each append, record the current capacity, whether a resize happens, and the number of elements copied during that resize. Sum the total copies and compute the amortized cost per append.
Continue learning
Previous: Big-O Notation
Previous: Common Complexities
Previous: Logarithms & Powers of Two