3.1.5 · D2Complexity Analysis

Visual walkthrough — Amortized analysis — aggregate, accounting, potential methods

3,142 words14 min readBack to topic

Step 1 — What a dynamic array actually is (the boxes)

WHAT we are looking at: a strip of boxes, with the leftmost of them shaded (holding data) and the rest empty (reserved but unused).

WHY these two numbers: because the entire cost drama lives in the gap . When the gap is , a push is trivial — drop your item in the next empty box. When the gap hits (array is full, ), there is nowhere to put the next item, and something expensive must happen. Everything below is about that moment.

PICTURE:

Figure — Amortized analysis — aggregate, accounting, potential methods

Step 2 — The cheap push vs. the expensive push

WHAT: the picture shows both events side by side. On the left, a green arrow drops one item into a waiting empty box. On the right, the strip is full, so we allocate a longer strip and drag every old item over (the orange copy arrows) before we can place the new one.

WHY we must copy: memory is contiguous — the reserved boxes sit next to each other physically. To get a bigger contiguous block we must ask for fresh memory elsewhere and physically move the data. There is no way to "just extend" the old strip.

PICTURE:

Figure — Amortized analysis — aggregate, accounting, potential methods

Step 3 — The key design choice: double the capacity

WHAT: the picture is a timeline of pushes. Each tick is one push. The rare tall orange bars are the resizes (at ); every other tick is a short green bar (a cheap write).

WHY doubling wins (in one sentence): doubling turns the sizes of the expensive copies into a geometric series ( — each term doubles), and the whole magic below is that a geometric series is dominated by its last (biggest) term — it is not the sum of many big terms like the arithmetic sum above.

PICTURE:

Figure — Amortized analysis — aggregate, accounting, potential methods

Step 4 — Aggregate method: add up ALL the copy costs

WHAT: stack the copy costs as nested bars. Each bar is (just barely) taller than all the shorter bars stacked together — so the grand total is a hair under twice the tallest bar.

WHY this kills the " per push" myth: yes, one copy is huge, but the huge ones are so rare that all copies summed is only , not .

Here is the total real work for the whole sequence, and (read "c-hat") is the amortized cost — the total shared evenly across the pushes.

PICTURE:

Figure — Amortized analysis — aggregate, accounting, potential methods

Step 5 — Accounting method: prepay with coins

WHAT: each newly pushed item arrives holding 3 coins. It spends 1 to write itself into its box, then keeps 2 coins in reserve.

PICTURE:

Figure — Amortized analysis — aggregate, accounting, potential methods

Step 6 — Potential method: energy stored in the structure

We now walk all three regimes of a push, using the piecewise above.

WHAT — Case A: cheap push that stays at/above half full (before: , after: still , capacity unchanged): The real write costs ; potential rises by (we banked energy). Total .

WHAT — Case B: cheap push while below half full (before: ). Here . After the push ; potential is either still (if ) or becomes (if it just reached half full). In the stays-below sub-case: Even cheaper than — the bound still holds. In the just-crosses-half sub-case, jumps from to . Because we just reached , that jump is at most , so again. This is exactly why the "symmetric" branch is simply : setting the below-half potential to zero can only make the drop into a copy larger and the rises smaller, so every push still satisfies , and the telescoping sum (below) is untouched.

WHAT — Case C: expensive resizing push (full: before-state so ; copy , then , , and now vs new capacity gives ? we have , true, so use the branch): The huge real cost is cancelled by the potential crashing from down to . The stored spring released exactly enough energy to pay for the copy.

All three cases give . ✓ — same answer as the aggregate and accounting methods.

PICTURE:

Figure — Amortized analysis — aggregate, accounting, potential methods

Step 7 — The degenerate & edge cases (never leave a gap)

  • The very first push (, ): the array is not full (), so the first push is a cheap write into the single box, cost . No copy. starts at (below half full), stays consistent with Step 6 Case B.
  • not a power of two (e.g. , so ): the last resize happened at , and we simply stop mid-strip. The sum still holds — the geometric bound never needed to be a nice number.
  • Empty sequence (): total cost , no operations, bound holds vacuously.
  • A push right after a resize (deep below half full): immediately after doubling, is small relative to the new , so the raw expression could momentarily be negative. This is exactly the region where the piecewise definition uses instead (Step 6 Case B), never a negative number. We proved there that in every below-half sub-case, so nothing breaks.

WHAT: the figure lines up all four edge cases as mini-strips with their costs labelled, so you can confirm each one obeys (or costs nothing).

PICTURE:

Figure — Amortized analysis — aggregate, accounting, potential methods

The one-picture summary

Figure — Amortized analysis — aggregate, accounting, potential methods

This final figure stacks all three viewpoints of the same sequence of pushes:

  • Aggregate (bottom): rare tall copy-bars whose total height is under .
  • Accounting (middle): each push carrying 3 coins, the piggy bank never emptying below zero.
  • Potential (top): the energy (or below half full) sawtoothing upward on cheap pushes, then crashing to pay each copy.

All three cross the finish line at the same flat line .

Recall Feynman retelling — the whole walkthrough in plain words

You have a row of boxes, starting with just one empty box (, ). Usually you just plop your next thing into the next empty box — one unit of effort, done. But every so often you run out of boxes, and then you must buy a strip twice as long and carry everything over — a big, sweaty job. It feels like pushing is expensive.

But watch how often the sweaty job happens: only when you hit sizes — each time farther apart than the last. Add up all the carrying you ever do and it comes to less than twice the number of pushes. So spread across everything, each push cost you about 3 units, flat. That's the aggregate view: add it all, divide.

Prefer coins? Charge yourself 3 coins per push, spend 1, save 2. Between one doubling and the next you push items and bank coins — exactly what the next copy of items costs. One coin per push would only bank and fall short; that's why it must be two. The bank never goes negative. That's accounting.

Prefer physics? Picture a spring that you wind up 2 notches on every cheap push once the array is half full (and just leave slack — zero energy — while it's still emptier than half). When the array fills, the spring is wound to notches — and releasing it delivers precisely the energy needed to copy the items. That's potential: , and the crash in eats the big cost.

Three stories, one truth: occasionally huge, always cheap on average, guaranteed.


Connections

  • Dynamic Arrays / Table Doubling — the structure whose growth we drew here.
  • Geometric Series — the engine behind Step 4.
  • Big-O, Big-Omega, Big-Theta is still asymptotic notation.
  • Disjoint Set Union (Union-Find), Splay Trees, Fibonacci Heaps — where the potential method of Step 6 becomes indispensable.
  • Parent topic ↑