Lowest Common Ancestor (LCA)
Lowest Common Ancestor (LCA)
1. Definition
LCA (Lowest Common Ancestor) refers to finding, in a rooted tree, the nearest (deepest) common ancestor of two given nodes x and y.
Using the reference tree below as an example:
LCA(5, 8) = 1LCA(6, 10) = 3
Reference Tree
1
/ \
2 3
/ | \ | \
11 4 5 6 7
|
8
/ \
9 10
2. Naive Solution
Steps:
- Preprocess the depth
depof every node. - To find the LCA of
xandy:- If
dep[x] > dep[y]: climbxupward untildep[x] == dep[y]. - If
dep[x] < dep[y]: climbyupward untildep[x] == dep[y]. - If
dep[x] == dep[y]: climbxandyupward together untilx == y. At this point we have found the LCA.
- If
Drawback: This is essentially a brute-force approach. When the tree degenerates into a chain (linked-list shape), the time cost becomes too large.
3. Speeding Up With Binary Lifting
The naive approach moves up only 1 step at a time. Can we move up faster?
Idea — Binary Lifting: move up by 1, 2, 4, 8, … steps at once.
Similar to a Sparse Table (ST table), we preprocess, for each node, its 1-step ancestor, 2-step ancestor, 4-step ancestor, and so on. Based on this table, the binary-lifting method finds the LCA in O(log n) time.
Algorithm steps:
- Preprocess the array
p[u][i], defined as the node reached by movinguupward by2^isteps. - Use the
parray to optimize the naive algorithm.
4. Preprocessing the p Array
- Moving from node
fato nodeu:p[u][0] = fa - Recurrence:
p[u][i] = p[p[u][i-1]][i-1] - Intuition: moving
uup2^isteps = movinguup2^(i-1)steps to reach some node, then moving that node up another2^(i-1)steps.
def dfs(u, fa):
# Preprocessing
deep[u] = deep[fa] + 1
p[u][0] = fa
for i in range(1, 21):
p[u][i] = p[p[u][i - 1]][i - 1]
for v in G[u]:
if v == fa:
continue
dfs(v, u)
5. Solving LCA(x, y)
Assume deep[x] > deep[y]:
Step 1 — Bring x up so that deep[x] == deep[y]
Use binary lifting: enumerate i from large to small. Try moving x up 2^i steps to a node p. If deep[p] < deep[y] (the move would overshoot above y’s level), do not move; otherwise, move.
Step 2 — Now deep[x] == deep[y]
- If
x == y, then the answer isx. - Otherwise, climb both up together. Enumerate
ifrom large to small. Movexup2^isteps topx, andyup2^isteps topy:- If
px == py, they share a common ancestor, but we cannot guarantee it is the lowest common ancestor, so do not move. - If
px != py, then move.
- If
This eventually lands x and y on the first pair of nodes that are not the common ancestor (i.e., just below the LCA). The LCA is then p[x][0].
6. Implementation
def lca(x, y):
# Ensure x is the deeper node
if deep[x] < deep[y]:
x, y = y, x
# Use binary lifting to climb up so that deep[x] == deep[y].
# Enumerate the step size 2^i from large to small.
# If the node reached after this move, p[x][i], still has
# depth >= deep[y], then the move is allowed.
for i in range(20, -1, -1):
if deep[p[x][i]] >= deep[y]:
x = p[x][i]
# At this point deep[x] == deep[y]
if x == y:
return x
# Climb up together: if moving 2^i steps makes both nodes share
# the same ancestor, the move is not allowed; otherwise it is.
for i in range(20, -1, -1):
if p[x][i] != p[y][i]:
x, y = p[x][i], p[y][i]
return p[x][0]