Patterns/Part II - Linked Structures/Binary Lifting

Pattern Reference

Binary Lifting

"Jump pointers for ancestors, LCA, path queries in O(log n)."

Loading...

Deep Dive Tutorial

Binary lifting precomputes up[v][k] = 2^k-th ancestor of v. Build: up[v][0]=parent[v]; for k≥1: up[v][k]=up[up[v][k-1]][k-1]. O(n log n) build, O(log n) query. LCA: equalize depths by lifting the deeper node, then lift both simultaneously until they meet. Extension: store min/max/sum along the path in the lifting table — O(log n) path aggregate queries.

Binary lifting for LCA and kth ancestor
const LOG = 18; // log2(10^5) ≈ 17

function buildBinaryLifting(n, parent, root = 0) {
    const up = Array.from({length: n}, () => new Array(LOG).fill(-1));
    const depth = new Array(n).fill(0);

    // BFS to set parent and depth
    const queue = [root];
    const visited = new Array(n).fill(false);
    visited[root] = true;
    while (queue.length) {
        const v = queue.shift();
        up[v][0] = parent[v] ?? -1;
        for (let k = 1; k < LOG; k++) {
            if (up[v][k-1] !== -1) up[v][k] = up[up[v][k-1]][k-1];
        }
        for (const u of (adj[v] || [])) {
            if (!visited[u]) {
                visited[u] = true;
                depth[u] = depth[v] + 1;
                parent[u] = v;
                queue.push(u);
            }
        }
    }
    return { up, depth };
}

function kthAncestor(v, k, up) {
    for (let i = 0; i < LOG; i++) {
        if ((k >> i) & 1) {
            v = up[v][i];
            if (v === -1) return -1;
        }
    }
    return v;
}

function lca(u, v, up, depth) {
    if (depth[u] < depth[v]) [u, v] = [v, u];
    // Bring u to same depth as v
    u = kthAncestor(u, depth[u] - depth[v], up);
    if (u === v) return u;
    // Binary lift both until they diverge
    for (let k = LOG - 1; k >= 0; k--) {
        if (up[u][k] !== up[v][k]) {
            u = up[u][k];
            v = up[v][k];
        }
    }
    return up[u][0];
}

Worked Problems

move-up
Binary lifting complexity: O(n log n) preprocess, O(log n) per query. LOG = ceil(log2(n)) — use 18 for n ≤ 10^5, 20 for n ≤ 10^6.

LCA applications:
- Path sum from u to v: sum[u] + sum[v] - 2*sum[LCA]
- Distance: depth[u] + depth[v] - 2*depth[LCA]
- Path min/max: extend lifting table to store edge weights

Euler tour + sparse table: Alternative for static trees — O(n) preprocess, O(1) LCA query. Better for offline many-query scenarios.