Bound the total cost of a sequence of operations, even when individual operations are expensive. An operation may cost occasionally, yet the average over any sequence is .

The three methods

1. Aggregate

Bound the total cost of operations directly, then divide.

Example: a monotonic stack over elements. Each element is pushed once and popped at most once, so the total is regardless of how long any single while loop runs.

2. Accounting (the banker’s method)

Charge each operation a fixed “fee”; cheap operations bank credit that expensive ones spend.

Example: vector::push_back. Charge 3 units per insertion — 1 to insert, 2 banked. When the array doubles, every element in the old half has 2 banked units, exactly paying for the copy.

3. Potential

Define with . The amortized cost is

and since never goes negative, .

Example: segment tree beats, where counts the distinct values across all nodes. Each expensive recursion merges two values, decreasing .

Where it appears

Structure / algorithmAmortized boundArgument
vector::push_backdoubling; accounting
DSUpath compression + union by size
Monotonic stack/queueeach element pushed and popped once
Two pointerspointers only move forward
Prefix function, Z-functionthe pointer never decreases below its total increase
Kasai decreases at most times
Splay tree / link-cut treepotential = sum of subtree sizes
Segment tree beatspotential = distinct values
Hash table with rehashingdoubling
Ukkonen’s algorithm per characterthe active length is bounded
Fibonacci heap decrease-keypotential = trees + marked nodes
”Next free slot” DSUeach slot is consumed once
Small-to-large merging per elementeach element moves times

The two workhorse arguments

”Each element is touched times”

The monotonic stack, two pointers, and the prefix function all rest on this. The inner while loop can run long, but every iteration removes something that can only be added once.

for (int i = 0; i < n; i++) {
    while (!st.empty() && cond(st.top(), a[i])) st.pop();   // total pops <= total pushes = n
    st.push(a[i]);
}

”Each element moves times”

Small-to-large merging: when merging two sets, always move the smaller into the larger. An element’s set at least doubles each time it moves, so it moves at most times. Total .

This one argument justifies DSU on tree, merging sets in tree DP, and union by size.

Amortized vs worst case — when it matters

SettingAmortized acceptable?
Total time limit (all contests)yes
Real-time systemsno — a single slow operation may miss a deadline
Interactive problems with per-query limitsusually yes
Persistent structuresamortized bounds often break under persistence

The last row is a genuine subtlety: amortized analysis assumes a single timeline. If an old version can be re-used repeatedly (as in persistence), the banked credit is spent many times and the bound fails. Splay trees, for instance, do not remain amortized when made persistent.

Proving your own bound

  1. Identify what makes an operation expensive.
  2. Find a quantity that the expensive operation reduces.
  3. Bound how much that quantity can increase per cheap operation.
  4. Since it starts bounded and never goes negative, the total expensive work is bounded.

That is the potential method, and it is usually the easiest of the three to apply to a new structure.

See also: Monotonic Stack · DSU · Complexity Theory