Skip to main content

lowest common ancestor

·465 words·3 mins
note cp algorithm tree
Table of Contents

in a rooted tree, how to find the lowest common ancestor of two nodes?

linear
#

we can do it in linear time. first move the deeper node to the same level as the another node. then move two nodes upward simultaneously until they meet.

def lca(u, v):
    # to ensure u is always deeper
    if depth[u] < depth[v]:
        u, v = v, u

    while depth[u] > depth[v]:
        u = up[u][0]

    while u != v:
        u = up[u][0]
        v = up[u][0]

    return u

but if both the number of nodes deck_b29acba03e17f19253981ef97847eee7f483a3e0.svg and the number of queries deck_e6f85db3eab8ceace463edf0b385d7003f2fab9e.svg is large (e.g. deck_6f3c42fed56488e0f63789680f9604ccf6070375.svg), we may use binary lifting to achieve faster result.

logarithmic
#

we can move the deeper node to the same level as the another node using binary lifting.

then we want to move the two nodes upward at the same time. let’s try the power of deck_3dff770fc1881afa389da64b39e70331578df110.svg from large to small (I know it is kind of random but this is maths). if they collide, they the node might be the LCA, might not be the LCA (then the LCA is below). if they do not collide, we must know that the LCA is above. hence we would like to jump only when they do not collide, otherwise we might jump over the LCA.

after the process, they must be the children of the LCA (since every integer have binary representation if that makes sense). therefore we can return the parent of either node.

note that we have to check if they are the same node after the initial jump, otherwise it would return the parent of LCA, which is the wrong answer.

a second note is the process is somehow similar to binary search. we are asking the question will the two nodes collide after they jump deck_9360c6ba8176a3c2edfb2711939e3370f952ec2b.svg nodes upward and the answer for each deck_9360c6ba8176a3c2edfb2711939e3370f952ec2b.svg is [False, ..., False, True, ..., True]. binary search is capable to find the intecepting point where the stream of False change to the stream of True. since the LCA is the first True, we can use binary search to do the job.

the time complexity is deck_ce26c0eeb3c69b1f51a640e6ea819e34747e231f.svg in the preprocessing and deck_d51d3ce524d11e1a5f2e0849e52fe09aadfa6e40.svg in the queires.

implementation
#

def lca(u, v):
    # to ensure u is always deeper
    if depth[u] < depth[v]:
        u, v = v, u

    k = depth[u] - depth[v]
    for j in range(LOG):
        if ((k >> j) & 1):
            u = up[u][j]

    if u == v:
        return u

    for j in range(LOG)[::-1]:
        if up[u][j] != up[v][j]:
            u = up[u][j]
            v = up[v][j]

    return up[u][0]

related problems #

further readings
#

Binary Lifting and LCA

references
#

LCA – Lowest Common Ancestor