Summary

Introduces amortized analysis through the union find data structure. Three methods — aggregate, accounting, and potential — provide tools for bounding average cost over a sequence of operations. Union by rank gives 𝑂(log𝑛); adding path compression gives 𝑂(log𝑛) amortized via the potential method.


Union Find

We want a data structure for maintaining disjoint sets:

DS={𝑆1,,𝑆𝑟}

where 𝑆𝑖𝑆𝑗= for all 𝑖𝑗. Each 𝑆𝑖 has a representative REP[𝑆𝑖]. We support 3 operations:

  1. Make-set(x) — add {𝑥} to DS, with REP[{𝑥}]=𝑥
  2. Find-set(x) — return REP[𝑆(𝑥)]
  3. Union(x,y) — replace 𝑆(𝑥) and 𝑆(𝑦) with 𝑆(𝑥)𝑆(𝑦), set representative

Example

Make-set(1) {1}. Make-set(2) {2}. Make-set(3) {3}. Find-set(2) 2. Union(1,3) {1,3}. Representative is 1. Find-set(3) 1 since 1 is the representative of {1,3}.

Linked List Implementation

Represent each set as a doubly-linked list with the representative as the head.

  • Make-set(x) — create a new linked list with just 𝑥 (Θ(1))
  • Find-set(x) — follow prev pointers to the head (Θ(𝑛) worst case)
  • Union(x,y) — append one list to the other (Θ(𝑛))

Adding head/tail pointers reduces Find-set to Θ(1), but Union still requires remapping all pointers in one list — still 𝑂(𝑛) worst case. We need a better analysis; this is where amortized analysis comes in.

Weighted Union (Amortized 𝑂(log𝑛))

Definition

Amortized analysis bounds the worst-case average cost over a sequence of operations.

A data structure 𝐷 has amortized cost 𝑇 if any sequence of 𝑘 operations starting from 𝐷‘s initial state has total cost at most 𝑘𝑇.

In other words, for operations 𝑂1,,𝑂𝑘 with costs 𝑐1,,𝑐𝑘, we bound 𝑘𝑖=1𝑐𝑖𝑘.

The optimization: always merge the shorter list into the longer one, so we only remap pointers for the shorter list.

Methods of Amortized Analysis

  1. Aggregate Method — compute total cost over a sequence, divide by 𝑘. This is what we just did.
  2. Accounting Method — “save” credit during cheap operations to “spend” on expensive ones.
  3. Potential Method — define a potential function over states; cheap operations build potential, expensive ones drain it.

The Potential Method

Definition

The Potential Method uses a function Φ mapping each state to a number. For states 𝐷0𝐷1𝐷𝑘, define ΔΦ𝑖=Φ𝑖Φ𝑖1.

Note

Φ𝑖Φ0, i.e. we cannot drop below our starting potential.

The amortized cost of operation 𝑖 is ̂𝑐𝑖=𝑐𝑖+ΔΦ𝑖. Summing:

𝑘𝑖=1̂𝑐𝑖=𝑘𝑖=1𝑐𝑖+Φ𝑘Φ0

Since Φ𝑘Φ00:

𝑘𝑖=1̂𝑐𝑖𝑘𝑖=1𝑐𝑖

So the amortized costs upper-bound the actual costs.

Forest-of-Trees Implementation

Instead of linked lists, treat each set as a tree where the root is the representative.

  • Make-set(x) — create a single-node tree (Θ(1))
  • Find-set(x) — walk up to the root (Θ(𝑛) worst case, not necessarily balanced)
  • Union(x,y) — point one root to the other (Θ(1))

Example

Make-set(1) {1}. Make-set(2) {2}. Make-set(3) {3}. Union(1,3): point root of {3} to root of {1}. Now Find-set(3) follows parent pointer from 3 to 1 and returns 1.

Union by Rank

With union by rank, always point the shorter tree’s root to the taller tree’s root. This guarantees max height 𝑂(log𝑛), so Find-set and Union both take 𝑂(log𝑛).

Path Compression

During Find-set(x), as we traverse up to the root, we redirect every node along the path to point directly to the root. Future queries on these nodes become Θ(1).

Amortized Analysis with Path Compression

We claim the amortized cost of Find-set with path compression is 𝑂(log𝑛) using the potential method. Define:

Φ=𝑥log(𝑥.size)

where 𝑥.size is the number of nodes in the subtree rooted at 𝑥.

Make-set(x): actual cost =1. ΔΦ=log(1)=0. Amortized cost =1.

Find-set(x): let 𝑢0=𝑥,𝑢1,,𝑢𝑘=root be the nodes on the path. Actual cost =𝑘. After compression, 𝑢1,,𝑢𝑘1 all become children of the root. Each 𝑢𝑗 for 𝑗=1,,𝑘1 loses the subtree below it on the path, so its size decreases. The root’s size is unchanged. We get:

ΔΦ=𝑘1𝑗=1[log(𝑢𝑗.sizeafter)log(𝑢𝑗.sizebefore)]0

Since each 𝑢𝑗.sizeafter𝑢𝑗+1.sizebefore and sizes are nested, a telescoping argument gives:

̂𝑐=𝑘+ΔΦ1+2log(𝑛)=𝑂(log𝑛)

Union(x, y): actual cost =𝑂(1) (just redirect a root pointer). The root’s size increases, giving ΔΦlog(𝑛). Amortized cost =𝑂(log𝑛).

Inverse Ackermann Bound

The 𝑂(log𝑛) analysis above is not tight. Combining union by rank with path compression, the amortized cost per operation is 𝑂(𝛼(𝑛)), where 𝛼 is the inverse Ackermann function (Tarjan, 1975). Since 𝛼(𝑛)4 for any practically conceivable input, this is effectively 𝑂(1) per operation.

Intuitively: union by rank keeps trees shallow (𝑂(log𝑛) height), so Find-set paths are already short. Path compression then flattens those paths so that future queries skip straight to the root. The two optimizations reinforce each other. Rank limits how tall trees can grow, and compression aggressively shortcuts whatever height remains. After a few operations, almost every node points directly (or nearly directly) to the root, so subsequent operations cost almost nothing. The tiny residual cost that can’t be eliminated is captured by 𝛼(𝑛).

The Potential Method in Detail

The three methods for amortized analysis are:

  1. Aggregate — compute total cost over a sequence, divide by number of operations.
  2. Accounting — pay extra during cheap operations, save credit to spend on expensive ones.
  3. Potential — define a potential function over states; we will mostly focus on this method.

The potential method works by analogy to potential energy in physics. Assign a potential Φ𝑖=Φ(𝐷𝑖) to each state 𝐷𝑖 of the data structure as it evolves: 𝐷0𝐷1𝐷𝑛. The amortized cost of the 𝑖-th operation is the actual cost plus the change in potential:

̂𝑐𝑖=𝑐𝑖+Φ𝑖Φ𝑖1

Summing over all operations, this telescopes: 𝑛𝑖=1̂𝑐𝑖=𝑛𝑖=1𝑐𝑖+Φ𝑛Φ0. If we choose Φ so that Φ𝑖Φ0 for all 𝑖, then Φ𝑛Φ00, so the total amortized cost upper-bounds the total actual cost.

The intuition is that cheap operations increase the potential (storing “credit”), while expensive operations drain it (spending that credit) — similar to the accounting method. A well-chosen Φ makes the change in potential offset the actual cost, keeping each ̂𝑐𝑖 small.

Note that this is not average-case analysis, as we make no assumptions about input distribution. We are bounding the worst-case total cost of any sequence of operations.

Example — Dynamic List

We have a list with operations append and delete. When the list is full, we double the size and copy everything. When we reach 1/4 capacity, we halve it. Both resizing operations cost 𝑂(𝑛).

Let Φ(𝑚,𝑛)=max{2(𝑚𝑛/2),𝑛/2𝑚}, where 𝑛 is the array size and 𝑚 is the number of filled slots.

  • No resize: actual cost is 1, and ΔΦ2, so ̂𝑐𝑖=𝑂(1).
  • Double: actual cost is 𝑛. Before the resize Φ=max{2(𝑛/2),𝑛/2}=𝑛. After, Φ=max{2(𝑛+12𝑛/2),2𝑛/2𝑛1}=2. So ̂𝑐𝑖=𝑛+2𝑛=2=𝑂(1).
  • Halve: by a symmetric argument, ̂𝑐𝑖=𝑂(1) as well.