LCA in a tree using Binary Lifting Technique

04 Sep, 2021
Given a binary tree, the task is to find the Lowest Common Ancestor of the given two nodes in the tree. Let G be a tree then LCA of two nodes u and v is defined as the node w in the tree which is an ancestor of both u and v and is farthest from the root node.If one node is the ancestor of another one than that particular node is the LCA of those two nodes.Example:  
Input:  
Output: The LCA of 6 and 9 is 1. The LCA of 5 and 9 is 1. The LCA of 6 and 8 is 3. The LCA of 6 and 1 is 1.  
Approach: The article describes an approach known as Binary Lifting to find the Lowest Common Ancestor of two nodes in a tree. There can be many approaches to solve the LCA problem. We are discussing the Binary Lifting Technique, the others can be read from here and here. Binary Lifting is a dynamic programming approach where we pre-compute an array memo[1, n][1, log(n)] where memo[i][j] contains 2^j-th ancestor of node i. For computing the values of memo[][], the following recursion may be used  
memo state:  
   memo[i][j] = i-th node’s 2^(j)th ancestor in the path  
memo initialization:  
   memo[i][j] = memo[i][0] (first parent (2^0) of each node is given)
memo trans:
   memo[i][j] = memo[ memo [i][j – 1]]
meaning: A(i,2^j)=A( A(i , 2^(j-1) ) , 2^(j-1) ) 
To find the (2^j)-th ancestor of i, recursively find i-th node’s 2^(j-1)th ancestor’s 2^(j-1)th ancestor. (2^(j) = 2^(j-1) + 2^(j-1))
So:
memo[i][j] = parent[i] if j = 0 and memo[i][j] = memo[memo[i][j – 1]][j – 1] if j > 0.  
We first check whether a node is an ancestor of other or not and if one node is ancestor of other then it is the LCA of these two nodes otherwise we find a node which is not the common ancestor of both u and v and is highest(i.e. a node x such that x is not the common ancestor of u and v but memo[x][0] is) in the tree. After finding such a node (let it be x), we print the first ancestor of x i.e. memo[x][0] which will be the required LCA.Below is the implementation of the above approach:  
C++
Java
Python3
C#
Javascript
// C++ implementation of the approach#include <bits/stdc++.h>using namespace std; // Pre-processing to calculate values of memo[][]void dfs(int u, int p, int **memo, int *lev, int log, vector<int> *g){    // Using recursion formula to calculate    // the values of memo[][]    memo[u][0] = p;    for (int i = 1; i <= log; i++)        memo[u][i] = memo[memo[u][i - 1]][i - 1];    for (int v : g[u])    {        if (v != p)        {            lev[v] = lev[u] + 1;            dfs(v, u, memo, lev, log, g);        }    }} // Function to return the LCA of nodes u and vint lca(int u, int v, int log, int *lev, int **memo){    // The node which is present farthest    // from the root node is taken as u    // If v is farther from root node    // then swap the two    if (lev[u] < lev[v])        swap(u, v);     // Finding the ancestor of u    // which is at same level as v    for (int i = log; i >= 0; i--)        if ((lev[u] - pow(2, i)) >= lev[v])            u = memo[u][i];     // If v is the ancestor of u    // then v is the LCA of u and v    if (u == v)        return u;     // Finding the node closest to the root which is    // not the common ancestor of u and v i.e. a node    // x such that x is not the common ancestor of u    // and v but memo[x][0] is    for (int i = log; i >= 0; i--)    {        if (memo[u][i] != memo[v][i])        {            u = memo[u][i];            v = memo[v][i];        }    }     // Returning the first ancestor    // of above found node    return memo[u][0];} // Driver Codeint main(){    // Number of nodes    int n = 9;     // vector to store tree    vector<int> g[n + 1];     int log = (int)ceil(log2(n));    int **memo = new int *[n + 1];    for (int i = 0; i < n + 1; i++)        memo[i] = new int[log + 1];     // Stores the level of each node    int *lev = new int[n + 1];     // Initialising memo values with -1    for (int i = 0; i <= n; i++)        memset(memo[i], -1, sizeof memo[i]);     // Add edges    g[1].push_back(2);    g[2].push_back(1);    g[1].push_back(3);    g[3].push_back(1);    g[1].push_back(4);    g[4].push_back(1);    g[2].push_back(5);    g[5].push_back(2);    g[3].push_back(6);    g[6].push_back(3);    g[3].push_back(7);    g[7].push_back(3);    g[3].push_back(8);    g[8].push_back(3);    g[4].push_back(9);    g[9].push_back(4);    dfs(1, 1, memo, lev, log, g);    cout << "The LCA of 6 and 9 is " << lca(6, 9, log, lev, memo) << endl;    cout << "The LCA of 5 and 9 is " << lca(5, 9, log, lev, memo) << endl;    cout << "The LCA of 6 and 8 is " << lca(6, 8, log, lev, memo) << endl;    cout << "The LCA of 6 and 1 is " << lca(6, 1, log, lev, memo) << endl;     return 0;} // This code is contributed by// sanjeev2552
// Java implementation of the approachimport java.util.*;public class GFG {     // ArrayList to store tree    static ArrayList<Integer> g[];    static int memo[][], lev[], log;     // Pre-processing to calculate values of memo[][]    static void dfs(int u, int p)    {         // Using recursion formula to calculate        // the values of memo[][]        memo[u][0] = p;        for (int i = 1; i <= log; i++)            memo[u][i] = memo[memo[u][i - 1]][i - 1];        for (int v : g[u]) {            if (v != p) {                 // Calculating the level of each node                lev[v] = lev[u] + 1;                dfs(v, u);            }        }    }     // Function to return the LCA of nodes u and v    static int lca(int u, int v)    {        // The node which is present farthest        // from the root node is taken as u        // If v is farther from root node        // then swap the two        if (lev[u] < lev[v]) {            int temp = u;            u = v;            v = temp;        }         // Finding the ancestor of u        // which is at same level as v        for (int i = log; i >= 0; i--) {            if ((lev[u] - (int)Math.pow(2, i)) >= lev[v])                u = memo[u][i];        }         // If v is the ancestor of u        // then v is the LCA of u and v        if (u == v)            return u;         // Finding the node closest to the root which is        // not the common ancestor of u and v i.e. a node        // x such that x is not the common ancestor of u        // and v but memo[x][0] is        for (int i = log; i >= 0; i--) {            if (memo[u][i] != memo[v][i]) {                u = memo[u][i];                v = memo[v][i];            }        }         // Returning the first ancestor        // of above found node        return memo[u][0];    }     // Driver code    public static void main(String args[])    {         // Number of nodes        int n = 9;        g = new ArrayList[n + 1];         // log(n) with base 2        log = (int)Math.ceil(Math.log(n) / Math.log(2));        memo = new int[n + 1][log + 1];         // Stores the level of each node        lev = new int[n + 1];         // Initialising memo values with -1        for (int i = 0; i <= n; i++)            Arrays.fill(memo[i], -1);        for (int i = 0; i <= n; i++)            g[i] = new ArrayList<>();         // Add edges        g[1].add(2);        g[2].add(1);        g[1].add(3);        g[3].add(1);        g[1].add(4);        g[4].add(1);        g[2].add(5);        g[5].add(2);        g[3].add(6);        g[6].add(3);        g[3].add(7);        g[7].add(3);        g[3].add(8);        g[8].add(3);        g[4].add(9);        g[9].add(4);        dfs(1, 1);        System.out.println("The LCA of 6 and 9 is " + lca(6, 9));        System.out.println("The LCA of 5 and 9 is " + lca(5, 9));        System.out.println("The LCA of 6 and 8 is " + lca(6, 8));        System.out.println("The LCA of 6 and 1 is " + lca(6, 1));    }}
# Python3 implementation of the above approachimport math # Pre-processing to calculate values of memo[][]def dfs(u, p, memo, lev, log, g):         # Using recursion formula to calculate    # the values of memo[][]    memo[u][0] = p    for i in range(1, log + 1):        memo[u][i] = memo[memo[u][i - 1]][i - 1]             for v in g[u]:        if v != p:            lev[v] = lev[u] + 1            dfs(v, u, memo, lev, log, g) # Function to return the LCA of nodes u and vdef lca(u, v, log, lev, memo):         # The node which is present farthest    # from the root node is taken as u    # If v is farther from root node    # then swap the two    if lev[u] < lev[v]:        swap(u, v)             # Finding the ancestor of u    # which is at same level as v    for i in range(log, -1, -1):        if (lev[u] - pow(2, i)) >= lev[v]:            u = memo[u][i]                 # If v is the ancestor of u    # then v is the LCA of u and v            if u == v:        return v             # Finding the node closest to the    # root which is not the common ancestor    # of u and v i.e. a node x such that x    # is not the common ancestor of u    # and v but memo[x][0] is    for i in range(log, -1, -1):        if memo[u][i] != memo[v][i]:            u = memo[u][i]            v = memo[v][i]         # Returning the first ancestor    # of above found node            return memo[u][0] # Driver code # Number of nodesn = 9 log = math.ceil(math.log(n, 2))g = [[] for i in range(n + 1)] memo = [[-1 for i in range(log + 1)]            for j in range(n + 1)] # Stores the level of each node            lev = [0 for i in range(n + 1)] # Add edgesg[1].append(2)g[2].append(1)g[1].append(3)g[3].append(1)g[1].append(4)g[4].append(1)g[2].append(5)g[5].append(2)g[3].append(6)g[6].append(3)g[3].append(7)g[7].append(3)g[3].append(8)g[8].append(3)g[4].append(9)g[9].append(4) dfs(1, 1, memo, lev, log, g) print("The LCA of 6 and 9 is", lca(6, 9, log, lev, memo))print("The LCA of 5 and 9 is", lca(5, 9, log, lev, memo))print("The LCA of 6 and 8 is", lca(6, 8, log, lev, memo))print("The LCA of 6 and 1 is", lca(6, 1, log, lev, memo)) # This code is contributed by Bhaskar
// C# implementation of the approachusing System;using System.Collections.Generic; class GFG{     // List to store tree    static List<int> []g;    static int [,]memo;    static int []lev;    static int log;     // Pre-processing to calculate    // values of memo[,]    static void dfs(int u, int p)    {         // Using recursion formula to        // calculate the values of memo[,]        memo[u, 0] = p;        for (int i = 1; i <= log; i++)            memo[u, i] = memo[memo[u, i - 1],                                    i - 1];        foreach (int v in g[u])        {            if (v != p)            {                 // Calculating the level of each node                lev[v] = lev[u] + 1;                dfs(v, u);            }        }    }     // Function to return the LCA of    // nodes u and v    static int lca(int u, int v)    {        // The node which is present farthest        // from the root node is taken as u        // If v is farther from root node        // then swap the two        if (lev[u] < lev[v])        {            int temp = u;            u = v;            v = temp;        }         // Finding the ancestor of u        // which is at same level as v        for (int i = log; i >= 0; i--)        {            if ((lev[u] - (int)Math.Pow(2, i)) >= lev[v])                u = memo[u, i];        }         // If v is the ancestor of u        // then v is the LCA of u and v        if (u == v)            return u;         // Finding the node closest to the root        // which is not the common ancestor of        // u and v i.e. a node x such that        // x is not the common ancestor of u        // and v but memo[x,0] is        for (int i = log; i >= 0; i--)        {            if (memo[u, i] != memo[v, i])            {                u = memo[u, i];                v = memo[v, i];            }        }         // Returning the first ancestor        // of above found node        return memo[u, 0];    }     // Driver code    public static void Main(String []args)    {         // Number of nodes        int n = 9;        g = new List<int>[n + 1];         // log(n) with base 2        log = (int)Math.Ceiling(Math.Log(n) / Math.Log(2));        memo = new int[n + 1, log + 1];         // Stores the level of each node        lev = new int[n + 1];         // Initialising memo values with -1        for (int i = 0; i <= n; i++)            for (int j = 0; j <= log; j++)                memo[i, j] = -1;        for (int i = 0; i <= n; i++)            g[i] = new List<int>();         // Add edges        g[1].Add(2);        g[2].Add(1);        g[1].Add(3);        g[3].Add(1);        g[1].Add(4);        g[4].Add(1);        g[2].Add(5);        g[5].Add(2);        g[3].Add(6);        g[6].Add(3);        g[3].Add(7);        g[7].Add(3);        g[3].Add(8);        g[8].Add(3);        g[4].Add(9);        g[9].Add(4);        dfs(1, 1);        Console.WriteLine("The LCA of 6 and 9 is " +                                        lca(6, 9));        Console.WriteLine("The LCA of 5 and 9 is " +                                        lca(5, 9));        Console.WriteLine("The LCA of 6 and 8 is " +                                        lca(6, 8));        Console.WriteLine("The LCA of 6 and 1 is " +                                        lca(6, 1));    }} // This code is contributed by PrinciRaj1992
<script>     // JavaScript implementation of the approach         // ArrayList to store tree    let g;    let memo, lev, log;      // Pre-processing to calculate values of memo[][]    function dfs(u, p)    {          // Using recursion formula to calculate        // the values of memo[][]        memo[u][0] = p;        for (let i = 1; i <= log; i++)            memo[u][i] = memo[memo[u][i - 1]][i - 1];        for (let v = 0; v < g[u].length; v++) {            if (g[u][v] != p) {                  // Calculating the level of each node                lev[g[u][v]] = lev[u] + 1;                dfs(g[u][v], u);            }        }    }      // Function to return the LCA of nodes u and v    function lca(u, v)    {        // The node which is present farthest        // from the root node is taken as u        // If v is farther from root node        // then swap the two        if (lev[u] < lev[v]) {            let temp = u;            u = v;            v = temp;        }          // Finding the ancestor of u        // which is at same level as v        for (let i = log; i >= 0; i--) {            if ((lev[u] - Math.pow(2, i)) >= lev[v])                u = memo[u][i];        }          // If v is the ancestor of u        // then v is the LCA of u and v        if (u == v)            return u;          // Finding the node closest to the root which is        // not the common ancestor of u and v i.e. a node        // x such that x is not the common ancestor of u        // and v but memo[x][0] is        for (let i = log; i >= 0; i--) {            if (memo[u][i] != memo[v][i]) {                u = memo[u][i];                v = memo[v][i];            }        }          // Returning the first ancestor        // of above found node        return memo[u][0];    }         // Number of nodes    let n = 9;    g = new Array(n + 1);     // log(n) with base 2    log = Math.ceil(Math.log(n) / Math.log(2));    memo = new Array(n + 1);     // Stores the level of each node    lev = new Array(n + 1);    lev.fill(0);     // Initialising memo values with -1    for (let i = 0; i <= n; i++)    {        memo[i] = new Array(log+1);        for (let j = 0; j < log+1; j++)        {            memo[i][j] = -1;        }    }    for (let i = 0; i <= n; i++)      g[i] = [];     // Add edges    g[1].push(2);    g[2].push(1);    g[1].push(3);    g[3].push(1);    g[1].push(4);    g[4].push(1);    g[2].push(5);    g[5].push(2);    g[3].push(6);    g[6].push(3);    g[3].push(7);    g[7].push(3);    g[3].push(8);    g[8].push(3);    g[4].push(9);    g[9].push(4);    dfs(1, 1);    document.write("The LCA of 6 and 9 is " + lca(6, 9) + "</br>");    document.write("The LCA of 5 and 9 is " + lca(5, 9) + "</br>");    document.write("The LCA of 6 and 8 is " + lca(6, 8) + "</br>");    document.write("The LCA of 6 and 1 is " + lca(6, 1));     </script>
The LCA of 6 and 9 is 1
The LCA of 5 and 9 is 1
The LCA of 6 and 8 is 3
The LCA of 6 and 1 is 1
 
Time Complexity: The time taken in pre-processing is O(NlogN) and every query takes O(logN) time. So the overall time complexity of the solution is O(NlogN).Auxiliary Space: O(NlogN)  
princiraj1992
sanjeev2552
bpandey23012002
darkhash
decode2207
pankajsharmagfg
gaurav2146
Binary Tree
LCA
Data Structures
Dynamic Programming
Tree
Data Structures
Dynamic Programming
Tree
Writing code in comment?
Please use ide.geeksforgeeks.org,
generate link and share the link here.