Part I · Union–Find · week 1
Path compression
Every find already walks to the root, so pay one more pass and point the whole path at it. The result is essentially constant time per operation.
Run the animation, step by step → generated live from any input you type — nothing is pre-recorded
Cost and properties
| amortised | O(α(n)) — all but constant |
|---|---|
| m ops on n sites | c·(n + m lg* n) |
| optimal? | no linear-time algorithm exists |
| space | 2n |
Reference: Sedgewick & Wayne, §1.5; Tarjan 1975.
Path compression in Python
Runnable as-is, and written for reading. Watch the highlighted line move through it in the animation, or send it straight to the visualizer — it arrives with a test case ready to run, yours to edit.
class WeightedQuickUnionPathCompressionUF:
def __init__(self, n):
self.id = list(range(n))
self.sz = [1] * n
def find(self, p):
root = p
while root != self.id[root]: # pass 1: locate the root
root = self.id[root]
while p != root: # pass 2: flatten the path
nxt = self.id[p]
self.id[p] = root
p = nxt
return root
def union(self, p, q):
i, j = self.find(p), self.find(q)
if i == j:
return
if self.sz[i] < self.sz[j]:
self.id[i] = j
self.sz[j] += self.sz[i]
else:
self.id[j] = i
self.sz[i] += self.sz[j]
Why it works
The idea
The first pass of find already visited every node on the path to the root. Those nodes will be searched again later, and they all belong to the same component — so point them straight at the root while the information is free. Each find makes the structure permanently flatter: the algorithm improves the data structure as it reads it.
Do not write the one-liner
The tempting p, self.id[p] = self.id[p], root is wrong in Python: targets are assigned left to right, so p is rebound before self.id[p] is stored and you compress the wrong node. Keep the explicit nxt temporary.
The cost claim
Weighted quick-union with path compression performs m operations on n sites in time proportional to n + m lg* n — and lg* n is at most 5 for any n you can store. It is not linear (Fredman–Saks proved no linear-time algorithm exists for this problem), but it is indistinguishable from linear in practice.
What to watch
Step through a find on a deep node: the second loop rewrites each pointer on the way. Do the same find again — it now takes one hop. The classic alternative, path halving (id[p] = id[id[p]], one pass), earns the same bound with less code.
What to try in the animation
n sites, then a list of p-q pairs to connect. The default is tinyUF.txt from the course.
The animation is generated from the input box, in your browser — press Animate after editing it. To execute the Python itself, use Open in Visualizer above.
- tinyUF (course) —
n: 10 · unions: 4-3 3-8 6-5 9-4 2-1 8-9 5-0 7-2 6-1 1-0 - worst case: a path —
n: 9 · unions: 0-1 1-2 2-3 3-4 4-5 5-6 6-7 7-8 - two clusters —
n: 12 · unions: 0-1 2-3 0-2 4-5 6-7 4-6 8-9 10-11 8-10
The rest of Union–Find
- Quick-find Keep a component identifier for every site.
- Quick-union Reinterpret the same array as parent pointers.
- Weighted quick-union One comparison added to union guarantees no tree is ever deeper than lg n.