"Some operations are usually instant and occasionally brutal. Amortized analysis is how you charge them honestly: spread the rare brutal cost across all the cheap ones."
The Puzzle of list.append
Appending to a Python list is O(1). Except sometimes it isn't — sometimes appending one item makes Python copy the entire list to a bigger chunk of memory, which is O(n). So how can the docs honestly call append "O(1)"? The answer is a beautiful idea called amortized analysis: even though individual operations vary, the average cost per operation across a long sequence is what we report.
How a Dynamic Array Actually Grows
A list is backed by a fixed-size block of memory. When it fills up, the list doesn't grow by one slot — it doubles: allocates a block twice as big and copies everything over. That copy is O(n) and feels expensive. But watch the rhythm: after doubling to size 8, you get 4 free cheap appends before the next copy. After doubling to 16, you get 8 free. The expensive copies happen exponentially less often as the list grows.
Add it up: to append n items, the total copying work across all the doublings is about 2n — still O(n) total. Divide that total by the n appends and each append costs O(1) on average. That's amortized O(1): not every append is cheap, but they're cheap on average, guaranteed, because the expensive ones are rare by design.
The Banker's Intuition
Here's the mental model that makes it click. Imagine every cheap append secretly pays a little extra — say, three coins instead of one. Two coins go in a savings jar. When the rare expensive copy comes, the jar has exactly enough saved up to pay for it. No single operation ever overdraws. The expensive copy was "pre-paid" by all the cheap appends before it. That's why the average stays flat even though one operation occasionally spikes.
Amortized Is Not Worst-Case
Crucial caveat: amortized O(1) does not mean every append is O(1). One specific append — the one that triggers the copy — really is O(n). If you're writing real-time code where a single operation must never stall (a heart monitor, an audio buffer), amortized isn't good enough; you need worst-case guarantees, and you might pre-allocate to avoid the spike. For throughput (total work over time), amortized is exactly the right lens. Knowing which lens your problem needs is the skill.