CSE3144 Β· Advanced Data Structures Β· Jul–Nov Semester 2026 Β· Lecture 3 of 36 Β· CO CSE3144.1

Amortized Analysis Techniques

Aggregate, Accounting, and Potential methods β€” proving that "occasionally expensive" can still mean "cheap on average," with no probability involved.

Dr. Manu ShrivastavaCourse Instructor Β· Consultation Fri 2–5 PM, LHC 308F
40 minutesSession outcome: apply aggregate, accounting, and potential methods
L3 Β· 00 β€” Agenda ~43 min total

Today, minute by minute

Meet the running examples

00–07

Multipop stack & binary counter from zero β€” full traces, and their naive O(nΒ²) / O(nk) bounds.

Why those naive bounds lie

07–12

Two structural reasons the "n Γ— worst cost" habit overshoots.

What amortized analysis is β€” and is not

12–16

Worst-case over a sequence; no probability. How it relates to Big-O.

Method 1 Β· Aggregate

16–22

Total cost T(n), divide by n. Applied to the multipop stack and binary counter.

Method 2 Β· Accounting β€” plus the charging refinement

22–31

Overcharge cheap operations, bank the credit, spend it on expensive ones β€” then the 2-3 tree case where that alone isn't enough.

Method 3 Β· Potential

31–37

Credit becomes a function Ξ¦ of the structure's state. The telescoping trick.

The payoff: dynamic arrays

37–41

Why vector.push_back / ArrayList.add is O(1) β€” by all three methods.

Recap, where this returns, practice

41–43

Splay trees, Fibonacci heaps, and Union-Find all lean on today.

CSE3144 β€” Lecture 3
L3 Β· 01a β€” Running example A setup

Example A: the Multipop Stack, from zero

Refresher β€” what is a stack? (from CS 1301)

A pile of plates in the mess. You can only touch the top: place a plate on top (PUSH) or take the top plate off (POP). Last In, First Out β€” LIFO. Both touch one plate, so each costs 1 unit of work.

The new operation today: MULTIPOP(S, k) β€” "pop up to k elements in one call."

MULTIPOP(S, k)
  while not EMPTY(S) and k β‰  0
      POP(S)        // 1 unit per element removed
      k ← k βˆ’ 1

Read the loop guard carefully β€” it stops when either k elements are popped or the stack runs empty, whichever comes first. So its cost is:

cost of MULTIPOP(S, k) = min(|S|, k)

Example: MULTIPOP(S, 5) on a stack holding only 2 elements pops just 2 and stops. Cost = min(2, 5) = 2 β€” not 5.

Full trace β€” 6 operations, every step shown
#OperationStack after (top β†’ bottom)CostΞ£
1PUSH(10)1011
2PUSH(20)20, 1012
3PUSH(30)30, 20, 1013
4POP() β†’ 3020, 1014
5PUSH(40)40, 20, 1015
6MULTIPOP(3)empty38

Step 6 unrolled, iteration by iteration: k=3, pop 40 (k becomes 2) β†’ pop 20 (k becomes 1) β†’ pop 10 (k becomes 0, loop exits). Three pops, cost 3.

Total: 8 units for 6 operations β€” average 1.33 per operation, even though one operation alone cost 3. Hold that thought.

The naive worst-case bound β€” derived step by step
  • Question: what can a sequence of n mixed PUSH / POP / MULTIPOP operations cost in total?
  • Step 1 β€” worst cost of one operation: a MULTIPOP. How big can min(|S|, k) get? The stack can hold at most n elements (only n operations happened, each PUSH adds one). So one MULTIPOP costs at most n.
  • Step 2 β€” multiply: n operations Γ— worst cost n each = O(nΒ²) total.
  • Step 3 β€” smell test: for even one MULTIPOP to cost n, all n earlier operations must have been PUSHes β€” and then the sequence is over! The n operations cannot all be expensive MULTIPOPs. You can't pop what was never pushed. O(nΒ²) is correct but wildly loose.
CSE3144 β€” Lecture 3
L3 Β· 01b β€” Running example B setup

Example B: the k-bit Binary Counter, from zero

Refresher β€” counting in binary

A 4-bit counter is an array A[3] A[2] A[1] A[0] of 0s and 1s. A[0] is the ones place, A[1] the twos, A[2] the fours, A[3] the eights:

value = 8Β·A[3] + 4Β·A[2] + 2Β·A[1] + 1Β·A[0]

Counting works like a car odometer. In decimal, 199 + 1: the 9 rolls to 0 and carries, the next 9 rolls to 0 and carries, then 1 becomes 2 β†’ 200. Binary is identical, except digits roll over at 1 instead of 9: 0111 + 1 = 1000.

INCREMENT(A)   // cost = number of bits flipped
  i ← 0
  while i < k and A[i] = 1
      A[i] ← 0            // this 1 rolls over: carry
      i ← i + 1
  if i < k then A[i] ← 1  // carry lands here

In words: walk up from A[0], turning every 1 you meet into 0 (the carry rippling), and when you first meet a 0, turn it into 1 and stop. Cost = number of bits you touched.

Full trace β€” INCREMENT on 0111 (value 7)
Loop stepLooking atFoundActionCounter now
i = 0A[0]1set to 0, carry on0110
i = 1A[1]1set to 0, carry on0100
i = 2A[2]1set to 0, carry on0000
i = 3A[3]0loop exits; set A[3] ← 11000

Result: 0111 β†’ 1000, which is 7 β†’ 8. βœ“  Four bits were flipped, so this increment cost 4 β€” the worst possible on 4 bits.

Now increment again, 1000 β†’ 1001 (8 β†’ 9): the loop looks at A[0], finds 0, exits immediately, sets A[0] ← 1. Cost 1. The expensive increment zeroed the low bits β€” and made its successors cheap. Hold this thought: it becomes "Reason 1" on the next slide.

The naive worst-case bound β€” derived step by step
  • Question: what do n INCREMENTs cost in total, starting from all zeros?
  • Step 1 β€” worst cost of one increment: all k bits flip (as in 0111 β†’ 1000). So one INCREMENT costs at most k.
  • Step 2 β€” multiply: n increments Γ— k each = O(nk) total.
  • Step 3 β€” smell test: flipping all k bits requires the counter to read 0111…1 β€” and immediately afterwards the low bits are all 0 again. A[0] flips on every increment, but A[1] only on every 2nd, A[2] on every 4th, A[3] on every 8th… Most increments touch one or two bits. O(nk) is correct but loose.

Both naive bounds are honest O-bounds β€” the sin is looseness, not error. The three methods of this lecture will each prove the tight answer for both structures: O(1) amortized per operation, O(n) total.

CSE3144 β€” Lecture 3
L3 Β· 02 β€” Why the naive bounds lie ~5 min

The worst-case habit that overcharges you

So far in your courses you've bounded one execution of one operation, then multiplied: n operations Γ— worst cost each. That's exactly what we just did on the last two slides β€” and it gave O(nΒ²) for the stack and O(nk) for the counter, both of which felt too pessimistic against the traces we computed. The multiplication habit is always correct, but it can be wildly loose, for two distinct reasons:

Reason 1 β€” operations talk to each other

An expensive operation leaves the structure in a cheap state

The cost of operation i is not independent of operations 1…iβˆ’1: each operation reshapes the structure, and an expensive operation usually reshapes it in a way that makes the following operations cheap. Multiplying n Γ— worst-cost silently assumes each operation faces a freshly worst-case structure β€” which the previous expensive operation just destroyed.

Expensive operation…and the cheap state it leaves behind
MULTIPOP of 50 elements (cost 50)Stack is now nearly empty β€” the next MULTIPOP has almost nothing left to pop; it cannot also cost 50.
Counter increment 0111 β†’ 1000 (4 flips)The carry run reset the low bits to 0 β€” each of the next several increments flips just one bit.
Reason 2 β€” the worst case needs a run-up

n operations can't all hit their worst case in one sequence

An operation's worst case requires the structure to be in a specific extreme state β€” and reaching that state consumes operations from the same budget of n. The expensive event and its own setup compete for the same sequence, so the expensive event is necessarily rare.

For this to cost……the sequence must first spend
One MULTIPOP costing nn PUSHes at cost 1 each. So a length-n sequence fits at most one such MULTIPOP β€” never n of them. Naive bound charges n Γ— n; the truth is ≀ 2n.
One increment flipping all k bitsThe counter must read 0111…1, which occurs only once every 2kβˆ’1 increments. The worst case is rare by arithmetic, not by luck.

Put together: cheap operations fund the expensive ones β€” pushes fund the multipop, 1-bits fund the carry cascade. Amortized analysis is the bookkeeping that makes this funding explicit and provable.

Everyday analogy

The hostel mess card

You pay β‚Ή3,000 once a month, then eat "free" for 30 days. Is a meal expensive? The single-payment view says β‚Ή3,000. The honest view spreads (amortizes) it: β‚Ή100 per day, every day, guaranteed β€” no probability, no luck involved. Amortized analysis does exactly this to operation costs.

Where you've met this already

Union-Find in Kruskal's MST

Kruskal's does 2|E| find operations and |V|βˆ’1 unions. One find with path compression can be expensive β€” but it flattens the tree, so the next finds on that path are nearly free. Charging every find its worst case ignores this "one pays, many benefit" structure entirely.

CSE3144 β€” Lecture 3
L3 Β· 03 β€” Definition ~5 min

Amortized analysis, precisely

amortized cost of an operation  =  worst-case total cost of any sequence of n operations Γ· n

It guarantees the average performance of each operation in the worst case over the sequence β€” even when some operations in the sequence are far costlier than others.

"But we already have Big-O β€” isn't this the same thing?"

No β€” and the distinction is a favourite exam question. Asymptotic notation (O, Θ, Ξ©) is a language for describing how any quantity grows. It doesn't say which quantity you measured. Worst-case, average-case, and amortized are three different quantities β€” three different questions about the same operation β€” and each answer is then written in asymptotic notation:

AnalysisThe question it answersMeasured over
Worst-case"How bad can one single call be?"One operation, adversarial state
Average-case"What does a call cost on a random input?"One operation, probability over inputs
Amortized"What does each call cost when I run many β€” counting the total, worst sequence possible?"A whole sequence, no probability

So "worst-case Θ(n)" and "amortized O(1)" are not contradictory β€” they can both be true of the same operation at the same time, because they answer different questions.

One operation, three true statements β€” vector.push_back / ArrayList.add

Appending to a dynamic array holding n elements (the array doubles when full β€” full analysis on slide 11):

StatementValueWhy
Worst case of a single callΘ(n)If this call triggers the resize, it copies all n existing elements.
Best case of a single callΘ(1)There's a free slot β€” write one element, done.
Amortized, over any n callsO(1)Total work for n appends is provably < 3n, so each call's fair share is ≀ 3 β€” no matter how adversarial the sequence.

All three statements use asymptotic notation. All three are simultaneously true. If someone tells you only "push_back is O(n)," they haven't lied β€” they've answered the least useful of the three questions. When you write a loop with n appends, the amortized answer is the one that predicts your program's real running time: O(n) total, not O(nΒ²).

Amortized analysis IS
  • A worst-case guarantee β€” over any adversarial sequence.
  • Deterministic β€” holds for every run, not on average luck.
  • An upper bound: total amortized β‰₯ total actual, always.
It is NOT
  • Not average-case analysis β€” no probability distribution on inputs.
  • Not a claim about one operation β€” a single call may still be slow.
  • Not a replacement for Big-O β€” amortized costs are still expressed in Big-O; what changes is the quantity being bounded.

Exam trap: "amortized O(1)" and "average-case O(1)" are different claims. Hash-table lookup is average-case O(1) β€” probabilistic, can be beaten by bad luck or bad inputs. Dynamic-array append is amortized O(1) β€” deterministic, guaranteed for every sequence. Interviewers and exam setters love asking which is which.

CSE3144 β€” Lecture 3
M1

The Aggregate Method

Compute the total cost T(n) of the whole sequence directly, then divide: amortized cost = T(n) / n. Every operation gets the same amortized cost.

"Stop asking what one operation costs. Ask what they can possibly cost together."
L3 Β· 04 β€” Aggregate Β· Multipop Stack ~4 min

Count pops against pushes

T(n) ≀ npush + npop ≀ 2n ⟹ amortized cost = T(n)/n = O(1)

Sanity check with a concrete run (n = 8): PUSH a, PUSH b, PUSH c, PUSH d, MULTIPOP 3, PUSH e, PUSH f, MULTIPOP 3. Actual costs: 1+1+1+1+3+1+1+3 = 12 ≀ 2Γ—8 = 16. βœ“ The expensive MULTIPOP(3) was only possible because three earlier PUSHes each paid cost 1 to set it up.

CSE3144 β€” Lecture 3 Β· Aggregate method
L3 Β· 05 β€” Aggregate Β· Binary Counter ~3 min

Count flips per bit, not bits per flip

Flip the accounting: instead of "how many bits does increment i flip," ask "how many times does bit j flip across all n increments?"

BitFlips whenTotal flips
A[0]every incrementn
A[1]every 2nd⌊n/2βŒ‹
A[2]every 4th⌊n/4βŒ‹
A[i]every 2ⁱ-th⌊n/2β±βŒ‹
T(n) = Ξ£i=0kβˆ’1 ⌊n/2β±βŒ‹ < n Β· Ξ£i=0∞ 1/2ⁱ = 2n ⟹ O(1) amortized

Trace, 4-bit counter β€” flipped bits highlighted ( 0β†’1  /  1β†’0 ):

ValA3A2A1A0CostΞ£
1000111
2001023
3001114
4010037
5010118
60110210
70111111
81000415

After 8 increments: total 15 < 2Γ—8 = 16. βœ“ The costly jump 7β†’8 (4 flips) was "paid for" by the five cheap 1-flip increments around it.

CSE3144 β€” Lecture 3 Β· Aggregate method
M2

The Accounting Method

Charge each operation an invented "amortized price" β€” overcharge the cheap ones, park the surplus as credit on specific objects, and let the credit pay for expensive operations later.

"Every element pays for its own funeral at the moment it is born."
L3 Β· 06 β€” Accounting Β· The rules ~3 min

You may invent prices β€” under two laws

Let ci be the actual cost of operation i and Δ‰i your invented amortized cost. You are free to choose the Δ‰i β€” the analysis is valid only if:

Law 1 β€” solvency at the end
Ξ£ Δ‰i β‰₯ Ξ£ ci  (for the full sequence)

Total amortized must upper-bound total actual β€” otherwise your "worst case" isn't one.

Law 2 β€” solvency at every moment
Ξ£i≀t Δ‰i βˆ’ Ξ£i≀t ci β‰₯ 0  for every prefix t

The credit balance can never go negative β€” you cannot pay today's cost with credit you hope to earn tomorrow.

The skill is in choosing where to park the credit: on the pushed element, on the bit that became 1 β€” on the exact object that will trigger the future expensive work.

Why invent prices when aggregate already worked? Two reasons, previewed now and proven later in the course: accounting needs only a local, per-step check instead of a global sum over the whole sequence (the sum becomes impossible for splay trees), and it can give different operations different amortized costs (essential for Fibonacci heaps).

CSE3144 β€” Lecture 3 Β· Accounting method
L3 Β· 07 β€” Accounting Β· Multipop Stack ~3 min

Charge PUSH β‚Ή2: one to work, one to save

ci is the real, measured cost of operation i; Δ‰i ("c-hat") is the amortized cost we invent for it. Below, Δ‰ is fixed per operation type β€” every PUSH is invented the same price, every POP the same, and so on, regardless of where it sits in the sequence.

OperationActual cost cAmortized cost Δ‰
PUSH12 β€” overcharged
POP10 β€” undercharged
MULTIPOP(k)min(|S|, k)0 β€” undercharged

Each PUSH pays β‚Ή1 for its own work and tapes β‚Ή1 onto the element it pushed. When that element is later popped β€” directly or inside a MULTIPOP β€” the taped rupee pays for the pop. A MULTIPOP of 40 elements costs 40, and finds exactly 40 rupees waiting on those 40 elements.

a β‚Ή1
b β‚Ή1
c β‚Ή1
after 3 PUSHes β€” credit β‚Ή3
a β‚Ή1
after MULTIPOP(2) β€” cost 2 paid by taped coins; credit β‚Ή1
How Law 1 holds β€” Σ Δ‰i ≥ Σ ci

Sum the invented prices: Σ Δ‰i = 2 · (number of PUSHes), since only PUSH is ever charged anything. Sum the real work: Σ ci = (number of PUSHes) + (number of POPs, whether standalone or inside a MULTIPOP). Every popped element was pushed exactly once, so (number of POPs) ≤ (number of PUSHes). Therefore Σ ci ≤ 2·(number of PUSHes) = Σ Δ‰i. Law 1 holds β€” total invented cost never falls short of total real cost, for any sequence.

How Law 2 holds β€” the running balance never goes negative

At any prefix ending at operation t, Σi≤t Δ‰i − Σi≤t ci is exactly "total rupees taped on so far minus total rupees spent so far" β€” i.e. the rupees still taped onto elements currently on the stack. That equals the current stack size, a count of physical elements, which can never be negative. Law 2 holds at every single moment, not just at the very end.

Both laws hold ⇒ Δ‰ is a valid amortized cost ⇒ every PUSH / POP / MULTIPOP is amortized O(1), no matter how they are mixed or ordered.

CSE3144 β€” Lecture 3 Β· Accounting method
L3 Β· 08 β€” Accounting Β· Binary Counter ~2 min

A bit pays β‚Ή2 to become 1 β€” and never pays to fall back

Same notation as before: ci is the real cost of flip i (always 1 β€” one bit changes); Δ‰i is the amortized cost we invent, fixed per flip type below.

Flip typeActual cost cAmortized cost Δ‰
0 β†’ 1  (set)12 β€” β‚Ή1 works, β‚Ή1 sits on the bit
1 β†’ 0  (reset, the carry cascade)10 β€” paid by the coin sitting on the bit

The entire carry cascade β€” the expensive part of an increment β€” is a run of 1β†’0 resets, and every one of those 1-bits is already carrying a coin. What remains is at most one 0β†’1 set per increment. So each INCREMENT is charged at most β‚Ή2.

How Law 1 holds β€” Σ Δ‰i ≥ Σ ci

Sum the invented prices: Σ Δ‰i = 2 · (number of 0→1 sets), since only sets are ever charged anything. Sum the real work: Σ ci = (number of sets) + (number of resets) β€” every flip, set or reset, costs 1. A bit can only reset if it was set at some earlier point (it must become 1 before it can become 0 again), so (number of resets) ≤ (number of sets). Therefore Σ ci ≤ 2·(number of sets) = Σ Δ‰i. Law 1 holds for any sequence of increments.

How Law 2 holds β€” the running balance never goes negative

At any prefix ending at increment t, Σi≤t Δ‰i − Σi≤t ci is exactly "total rupees credited to bits so far minus total rupees spent so far" β€” i.e. the rupees still sitting on bits that are currently 1. That equals the number of 1-bits in the counter right now, a count that can never be negative. Law 2 holds at every single moment.

Both laws hold ⇒ Δ‰ is valid ⇒ INCREMENT is amortized O(1), regardless of the starting value or how many increments run.

Ask yourself (30 seconds, discuss with your neighbour): in the trace on slide 05, the jump 7β†’8 flipped four bits. Point to the three coins that paid for the three resets. Where were they deposited?

CSE3144 β€” Lecture 3 Β· Accounting method
L3 Β· 08a β€” Accounting's blind spot: the charging refinement ~3 min

Sometimes a coin taped today isn't enough by the time it's spent

Where plain accounting breaks

Try the accounting trick on a 2-3 tree: insert an element and tape an O(lg n) coin onto it, to be spent when that same element is later deleted β€” identical in spirit to the multipop stack's PUSH-prepays-a-POP move. Here it fails.

By the time the element is deleted, the tree may have grown to nβ€² > n. Deleting now costs O(lg nβ€²) β€” but the coin taped at insertion only covers the smaller, stale O(lg n). The coin comes up short.

The fix β€” bill the delete to a different operation

Don't charge a delete to the insert of the same element. Charge it to whichever insert most recently grew the tree to its current size nβ€² β€” a different operation, possibly far away in the sequence.

That insert can be charged at most once: for the tree to reach size nβ€² again after shrinking, another insert has to regrow it. DELETE becomes amortized O(1); INSERT absorbs its own O(lg n) work plus this one contingent future charge.

Naming the distinction

Accounting method: an operation prepays for its own future cost (PUSH prepays its own later POP). Charging method: an operation's cost is billed to some other operation β€” past or future β€” chosen so no single operation is ever billed twice. They're close cousins, and many treatments fold them together, but the 2-3 tree case above is exactly where the distinction earns its keep: "prepay for yourself" fails; "bill whoever caused the trouble" works.

Coming attraction: this same idea β€” define Ξ¦ as "the number of nodes one step from trouble" (a 3-node about to split) β€” is exactly how Lecture 11's B-Trees justify their split costs, and 2-3 trees are literally the smallest B-Tree. Same battery, taught here in miniature.

CSE3144 β€” Lecture 3 Β· Accounting method (advanced note)
M3

The Potential Method

Stop taping coins to objects. Store all prepaid work as one number β€” a potential Ξ¦ of the data structure's current state, like potential energy in physics.

"The accounting method with the bookkeeping centralized: one bank account, whose balance is a function of the structure's shape."
L3 Β· 09a β€” Potential Β· Intuition first, no math yet ~3 min

Think of it as a battery

The analogy

Meter at the wall socket, not at the device

An inverter at home: most of the day it charges quietly from the mains; during a power cut it discharges to run the fans. If you meter consumption at the fans, usage is spiky β€” nothing for hours, then a burst. If you meter at the wall socket, the draw is small and steady, because the burst was prepaid into the battery over many quiet hours.

The potential method meters your data structure at the wall socket. Ξ¦ ("phi") = the battery's charge level. Cheap operations do their small work plus a little charging (Ξ¦ rises). The expensive operation's burst is powered mostly by discharge (Ξ¦ falls). The steady wall-socket reading is the amortized cost:

amortized = actual work + change in battery charge
The bridge from the accounting method

We were already doing this β€” just with coins

Look back at the accounting method's bank balance. At every moment, the total credit could be read directly off the structure's current shape β€” no memory of history needed:

StructureCoins parked on…So total credit =
Multipop stackeach stacked element|S| β€” the stack size
Binary countereach 1-bitb β€” the number of 1s

The potential method's only new idea: skip the coins. Don't track who holds what β€” just define the balance directly as a function of the current state, and call it Ξ¦. One number replaces all the bookkeeping.

Watch it work β€” numbers before formulas (Ξ¦ = stack size)
#OperationActual cost cΞ¦ beforeΞ¦ afterΔΦAmortized Δ‰ = c + ΔΦ
1PUSH(a)101+12
2PUSH(b)112+12
3PUSH(c)123+12
4MULTIPOP(2)231βˆ’20
Totals56
  • Every PUSH reads 2 at the wall socket: 1 unit of real work + 1 unit of charging.
  • The MULTIPOP did 2 units of real work but reads 0 β€” entirely battery-powered (Ξ¦ dropped by exactly 2).
  • Total amortized 6 β‰₯ total actual 5. The spare 1 is not lost β€” it's the charge still in the battery (final Ξ¦ = 1, element a still stacked). Overestimating is allowed; that's Law 1 again.
CSE3144 β€” Lecture 3 Β· Potential method
L3 Β· 09b β€” Potential Β· Now the machinery ~4 min

The same idea, in symbols

Everything on the previous slide, formalized in three steps. Operations take the structure through states D0 β†’ D1 β†’ … β†’ Dn (D0 is the initial state; Di is the state after operation i, which had actual cost ci).

Step 1 β€” choose a battery gauge. Pick any function Ξ¦ that reads a single number off a state (Ξ¦(D) = stack size, Ξ¦(D) = number of 1-bits, …). This is the only creative act; the rest is mechanical.

Step 2 β€” define amortized cost as actual work plus the change in the gauge:

Δ‰i  =  ci + Ξ¦(Di) βˆ’ Ξ¦(Diβˆ’1)   (work + charging, or work βˆ’ discharge)

Step 3 β€” sum over the sequence and watch it collapse. Write out the sum for, say, three operations β€” every intermediate Ξ¦ appears once with + and once with βˆ’:

Δ‰1 + Δ‰2 + Δ‰3 = (c1 + Ξ¦(D1) βˆ’ Ξ¦(D0)) + (c2 + Ξ¦(D2) βˆ’ Ξ¦(D1)) + (c3 + Ξ¦(D3) βˆ’ Ξ¦(D2))
+Ξ¦(D₁) kills βˆ’Ξ¦(D₁), +Ξ¦(Dβ‚‚) kills βˆ’Ξ¦(Dβ‚‚) β€” a telescoping sum. Only the two ends survive:
Ξ£ Δ‰i = Ξ£ ci + Ξ¦(Dn) βˆ’ Ξ¦(D0)
The one condition β€” and why

We need Ξ£ Δ‰i β‰₯ Ξ£ ci (Law 1). By the boxed equation, that holds exactly when Ξ¦(Dn) β‰₯ Ξ¦(D0) β€” and since the sequence can stop at any point, we demand it at every step: Ξ¦(Di) β‰₯ Ξ¦(D0) = 0 for all i. In battery language: the battery starts empty and may never be borrowed below empty. A battery that could go negative would let the wall-socket meter understate the true consumption.

Reading ΔΦ

ΔΦ > 0 β†’ operation overcharged: it did its work and also charged the battery (every PUSH: Δ‰ = 1 + 1 = 2).

ΔΦ < 0 β†’ operation undercharged: the battery discharged to pay most of its real cost (MULTIPOP: Δ‰ = kβ€² βˆ’ kβ€² = 0).

How do you choose Ξ¦? Rule of thumb: Ξ¦ = "how much trouble is stored in the structure right now" β€” the amount of future expensive work the current state can trigger. Big stack β†’ many pending pops. Many 1-bits β†’ a long carry is brewing. Choose Ξ¦ so that cheap operations raise it a little and expensive operations must drain it a lot; then the drain cancels their cost. The Ξ¦ you choose is the analysis β€” everything after that is the telescoping formula.

CSE3144 β€” Lecture 3 Β· Potential method
L3 Β· 10 β€” Potential Β· Both examples ~4 min

One line of Ξ¦, and both proofs fall out

Multipop stack β€” Ξ¦ = |S| (stack size)

Ξ¦(D0) = 0 (empty), Ξ¦ β‰₯ 0 always. βœ“

OpcΔΦĉ = c + ΔΦ
PUSH1+12
POP1βˆ’10
MULTIPOPkβ€²βˆ’kβ€²0

(kβ€² = min(|S|, k).) Every operation is amortized ≀ 2 ⟹ O(1). Note it reproduces the accounting method's prices β€” with zero creativity about where coins live.

Binary counter β€” Ξ¦ = b (number of 1-bits)

Ξ¦(D0) = 0 (all zeros), Ξ¦ β‰₯ 0 always. βœ“

Say increment i resets ti bits (the carry run) and sets at most one bit:

ci ≀ ti + 1
ΔΦ ≀ 1 βˆ’ ti
Δ‰i = ci + ΔΦ ≀ (ti + 1) + (1 βˆ’ ti) = 2

The ti β€” the entire expensive cascade β€” cancels algebraically. That cancellation is the whole method. INCREMENT is amortized O(1).

CSE3144 β€” Lecture 3 Β· Potential method
L3 Β· 11 β€” The payoff ~6 min

Dynamic arrays: the amortized O(1) you use every day

Refresher β€” what a dynamic array actually does

A dynamic array (C++ vector, Java ArrayList, Python list) keeps two numbers: n = elements stored, cap = slots allocated. Append works like this:

APPEND(x)
  if n = cap                    // table full?
      allocate new array of size 2Β·cap
      copy all n elements across  // cost n
      cap ← 2Β·cap
  write x into slot n; n ← n+1    // cost 1

So an append costs 1 normally, but n + 1 when it triggers a doubling. Worst case of one call: Θ(n). Yet every language calls append "O(1)" β€” amortized, and now we can prove it three ways.

Watch nine appends β€” spikes get rarer as they get bigger
Append #123456789
Capacity after1244888816
Actual cost123151119
Running total / 3n line1/33/66/97/1212/1513/1814/2115/2424/27

A cost-2Κ² spike happens only once every 2Κ² appends β€” rarity and size grow at exactly the same rate, so the running total never crosses the 3n ceiling. That "3" is about to appear in all three proofs.

Proof 1 Β· Aggregate

Total work of n appends = n unit writes + all the copying. Copies happen at sizes 1, 2, 4, 8, … so total copy work = 1 + 2 + 4 + … + 2⌊log(nβˆ’1)βŒ‹ < 2n (a geometric sum β€” each term is more than everything before it combined, and the last term is < n… so the sum is < 2n).

T(n) < n + 2n = 3n ⟹ 3 per append, O(1).

Proof 2 Β· Accounting β€” where "β‚Ή3" comes from

Stand at the moment just after a doubling: capacity 2m, m elements, all savings spent. Before the next doubling, exactly m more appends arrive. Charge each β‚Ή3: β‚Ή1 writes it, β‚Ή2 banked. At the next doubling, 2m elements must be copied, and the bank holds m Γ— β‚Ή2 = β‚Ή2m β€” exactly enough. Each new element's β‚Ή2 copies itself plus one old element whose savings died in the previous doubling.

Proof 3 Β· Potential β€” both cases verified

Ξ¦ = 2n βˆ’ cap (β‰₯ 0: right after a doubling the table is exactly half full, and it only fills from there).

Normal append: c = 1; n rises by 1, cap unchanged β†’ ΔΦ = +2; Δ‰ = 3.

Doubling append: just before, table is full: n = cap, so Ξ¦ = 2n βˆ’ n = n. The operation copies n and writes 1: c = n + 1. After: n+1 elements, cap = 2n β†’ Ξ¦ = 2(n+1) βˆ’ 2n = 2. So ΔΦ = 2 βˆ’ n, and Δ‰ = (n+1) + (2βˆ’n) = 3. The n's cancel β€” the same signature cancellation as the counter's carry run.

Verifying Proof 3 on the concrete 9-append trace above

Applying Ξ¦ = 2n βˆ’ cap to every single append from the table above β€” before and after each one β€” so there's no ambiguity about which n and cap go where. n before append i is always one less than n after (each append adds exactly one element); cap before append i is whatever cap after was left at by the previous append.

Append in beforeβ†’aftercap beforeβ†’afterΞ¦ beforeβ†’afterΔΦcΔ‰ = c+ΔΦ
10β†’10β†’10β†’1+112 (bootstrap)
21β†’21β†’21β†’2+123
32β†’32β†’42β†’2033
43β†’44β†’42β†’4+213
54β†’54β†’84β†’2βˆ’253
65β†’68β†’82β†’4+213
76β†’78β†’84β†’6+213
87β†’88β†’86β†’8+213
98β†’98β†’168β†’2βˆ’693

Append 1 is a harmless one-time exception β€” bootstrapping cap from 0 (nothing allocated yet) doesn't cleanly fit the doubling formula (doubling 0 gives 0, not 1), so it comes out at 2 instead of 3. From append 2 onward, every single one β€” cheap writes and the big resize spikes alike β€” reads exactly 3, whether the real cost is 1 or 9.

Three languages, one answer: every append is amortized cost 3 = O(1). And notice the battery reading: Ξ¦ = 2n βˆ’ cap is literally "how close to full are we" β€” trouble brewing toward the next resize.

CSE3144 β€” Lecture 3 Β· Application
L3 Β· 11b β€” Design guide ~3 min

How to invent Ξ¦ yourself: a four-step recipe

Every Ξ¦ in this lecture looks like magic until you see the pattern. Here is the pattern β€” use it on any new structure, in homework or exams:

Step 1 β€” Find the spike and its trigger

Which operation is occasionally expensive, and what state of the structure makes it fire? A full array fires a resize. A run of trailing 1s fires a carry cascade. A tall stack enables a big MULTIPOP.

Step 2 β€” Name the trouble

Define a measurable quantity that cheap operations build up and the spike consumes β€” chosen so the spike's actual cost β‰ˆ the amount of trouble it destroys. This is the creative step; everything else is arithmetic.

Step 3 β€” Set Ξ¦ = k Γ— trouble, then tune k

Compute Δ‰ for the spike; pick the constant k so the discharge cancels the spike's cost (the n's or tα΅’'s must cancel). Then check the ground rules: Ξ¦ = 0 at the start, Ξ¦ β‰₯ 0 always.

Step 4 β€” Verify every operation type

Make the little table: Δ‰ = c + ΔΦ for each operation. All entries should hit your target bound. If a cheap operation comes out expensive, your trouble measure is wrong β€” go back to Step 2.

StructureSpike & triggerTrouble measureΦWhy that constant
Multipop stackMULTIPOP β€” fires when the stack is tallpending pops = stacked elements|S|k = 1: each stored element will cost exactly 1 pop
Binary countercarry cascade β€” fires on trailing 1s1-bits waiting to be resetb = #1sk = 1: each 1-bit will cost exactly 1 reset
Dynamic arrayresize β€” fires when the table is fullelements added since the last resize = n βˆ’ cap/22n βˆ’ capk = 2: each new element must fund copying itself + one old element
Dynamic array's trouble measure, verified on the concrete trace

Every resize doubles capacity from C to 2C, and it only fires when the table is completely full (n = C). So immediately after any resize, the new cap is exactly twice whatever n was at that instant β€” meaning cap/2 always equals the element count the table had right around its most recent doubling, with no separate variable needed to remember when that was. "n βˆ’ cap/2" is just n measured against that self-updating baseline.

Moment (from the 9-append trace above)ncapn βˆ’ cap/2
Just resized (after append 3)341
One cheap append later (append 4)442
Just resized again (after append 5)581
Table completely full (after append 8)884
Just resized again (after append 9)9161

The value climbs by exactly 1 with every cheap append, then snaps back down the moment the next resize fires β€” because cap itself jumps up, dragging cap/2 up to meet n. Right when the table is completely full and about to trigger the next resize, n βˆ’ cap/2 peaks at exactly cap/2 β€” but that resize is about to copy all n = cap elements, twice as many as the trouble measure counted. That gap is exactly why k = 2: the trouble measure only tracks the newly-added half of the table, so each tracked element must fund copying itself and one untracked "old" element sitting alongside it.

Exam strategy: when a question gives you Ξ¦, your job is only Steps 3–4 β€” verify Ξ¦ β‰₯ 0, then compute Δ‰ = c + ΔΦ per operation type, showing the cancellation. When a question asks you to design the analysis, write the recipe explicitly: name the spike, name the trouble, state Ξ¦, verify. That structure earns method marks even if the constant needs adjusting.

CSE3144 β€” Lecture 3 Β· Design guide
L3 Β· 12 β€” The three methods, side by side ~1 min

Same truth, three vocabularies

AggregateAccountingPotential
Core moveBound total T(n), divide by nInvent per-operation prices; bank surplus as credit on objectsDefine Ξ¦(state); Δ‰ = c + ΔΦ
Amortized costsSame for every operationCan differ per operation typeCan differ per operation type
Correctness obligationThe counting argument itselfCredit never negative (Law 2)Ξ¦(Di) β‰₯ Ξ¦(D0) = 0
Feels likeArithmeticBookkeepingPhysics
ReachSimple structuresMedium β€” needs a good "where to park credit" storyMost powerful β€” Splay trees, Fibonacci heaps need this one

They are increasing generality, not competing truths: every accounting scheme corresponds to some Ξ¦, and any Ξ¦ can be read as a crediting scheme.

"Aggregate worked fine for everything today β€” why learn the other two?"

Fair objection β€” on today's toy structures the three methods are interchangeable, which is exactly why we practise all three here. On the advanced structures ahead, the aggregate method doesn't become wrong; it becomes unusable, in two ways:

Failure modeWhere you'll meet it
The global sum becomes intractable. Aggregate needs one counting argument bounding the total cost of any sequence ("pops ≀ pushes"). For a splay tree, the shape after operation i depends on the entire history β€” no such one-liner exists. The known proof is a potential-function argument. Lecture 10 Splay Trees
One averaged number becomes uninformative. Aggregate outputs a single blended cost that depends on the operation mix. But a Fibonacci heap's whole point is that different operations have different amortized costs: insert and decrease-key O(1), extract-min O(log n). Only accounting/potential can price operations individually β€” valid for every mix. Lecture 19 Fibonacci Heaps
Lecture 31 Union-Find

So if accounting and potential feel like extra ceremony today, park the doubt β€” you are learning the mechanics on easy examples because the structures that need them are too hard to learn the mechanics on. By Lecture 10 the ceremony will be the only thing standing.

CSE3144 β€” Lecture 3
L3 Β· 13 β€” Recap & what's next ~2 min

Three takeaways, three future appointments

Lecture 10

Splay Trees

O(log n) amortized via a beautiful (and tricky) potential function.

Lecture 19

Fibonacci Heaps

decrease-key in O(1) amortized β€” the potential method's signature result.

Lecture 31

Union-Find

Path compression: near-O(1) amortized β€” resolving today's opening puzzle.

CSE3144 β€” Lecture 3

Questions?

Dr. Manu Shrivastava β€” LHC 308F β€” Friday 2:00–5:00 PM

Next: Lecture 4 β€” External Sorting and the Memory Hierarchy.