Skip to content
Related Articles
Open in App
Not now

Related Articles

LCA in a tree using Binary Lifting Technique

Improve Article
Save Article
Like Article
  • Difficulty Level : Hard
  • Last Updated : 07 Dec, 2022
Improve Article
Save Article
Like Article

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++




// 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, vector<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 v
int lca(int u, int v, int log, vector<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 Code
int 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
    vector<int> lev(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




// Java implementation of the approach
import 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




# Python3 implementation of the above approach
import 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 v
def 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 nodes
n = 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 edges
g[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#




// C# implementation of the approach
using 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

Javascript




<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>

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

 

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) 


My Personal Notes arrow_drop_up
Like Article
Save Article
Related Articles

Start Your Coding Journey Now!