Articles

Amortized Analysis Explained: Average Cost Over Time

Amortized analysis measures the average cost of an operation over a sequence of calls, not its worst case. How dynamic array resizing gets O(1) amortized inserts.

The Lycoris Team The Lycoris Team · · 4 min read
A laptop screen showing code in a dark editor theme

Amortized analysis is a way of measuring an operation’s cost by averaging it over a whole sequence of calls, rather than judging it by its single worst-case execution. It answers a different question than ordinary worst-case analysis: not “how bad can one call be,” but “how bad can things get, on average, if this operation is called repeatedly.” The distinction matters because some data structures have operations that are occasionally expensive but so rarely expensive that their long-run average cost is still cheap.

The classic example: a growing array

Consider a dynamic array (JavaScript’s Array, Python’s list, or a C++ vector) that needs to grow when it runs out of room. A naive strategy would grow by one slot every time an element is added — but that requires allocating a new, slightly larger block of memory and copying every existing element into it, on every single insert. That makes each insert cost proportional to the array’s current size, which is expensive.

The standard fix is geometric growth: whenever the array is full, double its capacity instead of growing it by one. This means:

  • Most inserts are cheap — there’s spare capacity, so the new element just gets placed in the next open slot, an O(1) operation
  • Occasionally, an insert triggers a resize — copying every existing element to a new, larger block, an O(n) operation

Judged by worst case alone, insert is an O(n) operation, because that resize can happen. But resizes get exponentially rarer as the array grows — after doubling, there’s room for as many new cheap inserts as the array’s entire previous size before the next resize is needed. Summed across any long sequence of inserts, the total cost of all the resizes stays proportional to the total number of elements ever inserted, which works out to O(1) amortized cost per insert — the expensive operations are rare enough, and cheap enough in total, that they don’t change the average.

Why “amortized” isn’t the same as “average case”

It’s easy to conflate amortized analysis with average-case analysis, but they answer different questions. Average-case analysis is about the distribution of inputs — it assumes some inputs are more likely than others and averages over that likelihood, which says nothing about what happens for a specific bad input. Amortized analysis makes no assumption about input distribution at all; it’s a guarantee about any sequence of operations on the same structure, because the accounting is done against the sequence, not against a probability distribution. An amortized O(1) bound holds even for an adversarially chosen sequence of inserts — it’s a much stronger guarantee than an average-case bound, not a weaker one.

Three ways to prove an amortized bound

Formal amortized analysis usually uses one of three techniques:

  • Aggregate method — sum the total cost of a sequence of n operations and divide by n. This is what the doubling-array argument above does directly.
  • Accounting method — assign each operation a fixed “amortized cost,” overcharging cheap operations slightly and banking the surplus as credit to pay for the occasional expensive one.
  • Potential method — define a potential function over the data structure’s state, and account for an operation’s real cost plus the change in potential it causes, so expensive operations that decrease potential (like a resize resetting a growth counter) offset the cost they otherwise appear to have.

These are proof techniques, not competing algorithms — they all arrive at the same bound for a well-behaved structure like a doubling array; which one is easiest to apply depends on the structure being analyzed.

Where else amortized bounds show up

Dynamic arrays are the canonical example, but the same reasoning appears throughout common data structures. A hash table resizes its underlying bucket array the same way, giving amortized O(1) insertion even though an individual insert that triggers a rehash is O(n). Some linked-list-backed structures use amortized arguments to justify why occasional pointer restructuring doesn’t dominate their overall cost. Amortized reasoning is also how a stack or queue implemented with two arrays or a growable buffer can be shown to support push and pop in O(1) amortized time despite occasional internal reshuffling.

Why it matters in practice

Understanding amortized cost prevents a common mistake: assuming a data structure is slow because one operation, examined in isolation, looks expensive. It also explains a real practical hazard — a structure with excellent amortized behavior across a long sequence of operations can still have unpredictable latency on any single call, which matters for real-time systems where a single slow operation (a resize landing at the wrong moment) is a problem even if the average cost is fine. Knowing the difference between the Big O worst-case bound and the amortized bound is what lets you reason correctly about which one your situation actually cares about.

The takeaway

Amortized analysis measures the total cost of a sequence of operations divided by the number of operations, giving a guarantee that holds for any sequence — not an assumption about typical input. A doubling dynamic array is the textbook case: individual resizes are O(n), but they happen rarely enough that the amortized cost per insert is O(1). The distinction between worst-case, average-case, and amortized cost is one of the more commonly confused ideas in algorithm analysis, and knowing which one a claimed complexity bound actually refers to changes how much you should trust it for your specific use case.

The Lycoris Team The Lycoris Team · · 5 min read

What Is Big O Notation? Algorithm Complexity Explained

Big O notation describes how an algorithm's time or memory grows as input grows. The common classes, what they mean, and how to reason about them.

#Computer Science #Performance #Algorithms
Chisato Chisato · · 4 min read

What Is Virtual Memory? Paging and Address Translation

Virtual memory gives every process its own private address space, mapped to physical RAM by the OS and CPU — enabling isolation, swapping, and overcommit.

#Hardware #Computer Science #Performance
The Lycoris Team The Lycoris Team · · 4 min read

How Regular Expressions Work Under the Hood

Regular expressions are matched by finite automata or backtracking engines. How regex engines parse patterns, and why some patterns run slowly.

#Computer Science #Algorithms #Developer Tools