Open In App

LCA for general or n-ary trees (Sparse Matrix DP approach )

Improve
Improve
Improve
Like Article
Like
Save Article
Save
Share
Report issue
Report

In previous posts, we have discussed how to calculate the Lowest Common Ancestor (LCA) for a binary tree and a binary search tree (this, this and this). Now let’s look at a method that can calculate LCA for any tree (not only for binary tree). We use Dynamic Programming with Sparse Matrix Approach in our method. This method is very handy and fast when you need to answer multiple queries of LCA for a tree. 

lca-of-n-ary-tree

Pre-requisites:

  1. DFS 
  2. Basic DP knowledge (This and this
  3. Range Minimum Query (Square Root Decomposition and Sparse Table)

Naive Approach:- O(n) 
The naive approach for this general tree LCA calculation will be the same as the naive approach for the LCA calculation of Binary Tree (this naive approach is already well described here.

The implementation for the naive approach is given below:- 

C++




/* Program to find LCA of n1 and n2 using one DFS on
   the Tree */
#include<bits/stdc++.h>
using namespace std;
 
// Maximum number of nodes is 100000 and nodes are
// numbered from 1 to 100000
#define MAXN 100001
 
vector < int > tree[MAXN];
int path[3][MAXN]; // storing root to node path
 
// storing the path from root to node
void dfs(int cur, int prev, int pathNumber, int ptr,
                             int node, bool &flag)
{
    for (int i=0; i<tree[cur].size(); i++)
    {
        if (tree[cur][i] != prev and !flag)
        {
            // pushing current node into the path
            path[pathNumber][ptr] = tree[cur][i];
            if (tree[cur][i] == node)
            {
                // node found
                flag = true;
 
                // terminating the path
                path[pathNumber][ptr+1] = -1;
                return;
            }
            dfs(tree[cur][i], cur, pathNumber, ptr+1,
                                        node, flag);
        }
    }
}
 
// This Function compares the path from root to 'a' & root
// to 'b' and returns LCA of a and b. Time Complexity : O(n)
int LCA(int a, int b)
{
    // trivial case
    if (a == b)
        return a;
 
    // setting root to be first element in path
    path[1][0] = path[2][0] = 1;
 
    // calculating path from root to a
    bool flag = false;
    dfs(1, 0, 1, 1, a, flag);
 
    // calculating path from root to b
    flag = false;
    dfs(1, 0, 2, 1, b, flag);
 
    // runs till path 1 & path 2 matches
    int i = 0;
    while (path[1][i] == path[2][i])
        i++;
 
    // returns the last matching node in the paths
    return path[1][i-1];
}
 
void addEdge(int a,int b)
{
    tree[a].push_back(b);
    tree[b].push_back(a);
}
 
// Driver code
int main()
{
    int n = 8; // Number of nodes
    addEdge(1,2);
    addEdge(1,3);
    addEdge(2,4);
    addEdge(2,5);
    addEdge(2,6);
    addEdge(3,7);
    addEdge(3,8);
 
    cout << "LCA(4, 7) = " << LCA(4,7) << endl;
    cout << "LCA(4, 6) = " << LCA(4,6) << endl;
    return 0;
}


Java




/* Program to find LCA of n1 and n2 using one DFS on
the Tree */
 
import java.util.*;
 
class GFG
{
 
    // Maximum number of nodes is 100000 and nodes are
    // numbered from 1 to 100000
    static final int MAXN = 100001;
 
    static Vector<Integer>[] tree = new Vector[MAXN];
    static int[][] path = new int[3][MAXN]; // storing root to node path
    static boolean flag;
 
    // storing the path from root to node
    static void dfs(int cur, int prev, int pathNumber, int ptr, int node)
    {
        for (int i = 0; i < tree[cur].size(); i++)
        {
            if (tree[cur].get(i) != prev && !flag)
            {
                // pushing current node into the path
                path[pathNumber][ptr] = tree[cur].get(i);
                if (tree[cur].get(i) == node)
                {
                    // node found
                    flag = true;
 
                    // terminating the path
                    path[pathNumber][ptr + 1] = -1;
                    return;
                }
                dfs(tree[cur].get(i), cur, pathNumber, ptr + 1, node);
            }
        }
    }
 
    // This Function compares the path from root to 'a' & root
    // to 'b' and returns LCA of a and b. Time Complexity : O(n)
    static int LCA(int a, int b)
    {
        // trivial case
        if (a == b)
            return a;
 
        // setting root to be first element in path
        path[1][0] = path[2][0] = 1;
 
        // calculating path from root to a
        flag = false;
        dfs(1, 0, 1, 1, a);
 
        // calculating path from root to b
        flag = false;
        dfs(1, 0, 2, 1, b);
 
        // runs till path 1 & path 2 matches
        int i = 0;
        while (i < MAXN && path[1][i] == path[2][i])
            i++;
 
        // returns the last matching node in the paths
        return path[1][i - 1];
    }
 
    static void addEdge(int a, int b)
    {
        tree[a].add(b);
        tree[b].add(a);
    }
 
    // Driver code
    public static void main(String[] args)
    {
        for (int i = 0; i < MAXN; i++)
            tree[i] = new Vector<Integer>();
 
        // Number of nodes
        addEdge(1, 2);
        addEdge(1, 3);
        addEdge(2, 4);
        addEdge(2, 5);
        addEdge(2, 6);
        addEdge(3, 7);
        addEdge(3, 8);
 
        System.out.print("LCA(4, 7) = " + LCA(4, 7) + "\n");
        System.out.print("LCA(4, 6) = " + LCA(4, 6) + "\n");
    }
}
 
// This code is contributed by 29AjayKumar


Python3




# Python3 program to find LCA of n1 and
# n2 using one DFS on the Tree
 
# Maximum number of nodes is 100000 and
# nodes are numbered from 1 to 100000
MAXN = 100001
 
tree = [0] * MAXN
for i in range(MAXN):
    tree[i] = []
 
# Storing root to node path
path = [0] * 3
for i in range(3):
    path[i] = [0] * MAXN
 
flag = False
 
# Storing the path from root to node
def dfs(cur: int, prev: int, pathNumber: int,
        ptr: int, node: int) -> None:
             
    global tree, path, flag
 
    for i in range(len(tree[cur])):
        if (tree[cur][i] != prev and not flag):
 
            # Pushing current node into the path
            path[pathNumber][ptr] = tree[cur][i]
             
            if (tree[cur][i] == node):
 
                # Node found
                flag = True
 
                # Terminating the path
                path[pathNumber][ptr + 1] = -1
                return
 
            dfs(tree[cur][i], cur, pathNumber,
                ptr + 1, node)
 
# This Function compares the path from root
# to 'a' & root to 'b' and returns LCA of
# a and b. Time Complexity : O(n)
def LCA(a: int, b: int) -> int:
     
    global flag
     
    # Trivial case
    if (a == b):
        return a
 
    # Setting root to be first element
    # in path
    path[1][0] = path[2][0] = 1
 
    # Calculating path from root to a
    flag = False
    dfs(1, 0, 1, 1, a)
 
    # Calculating path from root to b
    flag = False
    dfs(1, 0, 2, 1, b)
 
    # Runs till path 1 & path 2 matches
    i = 0
    while (path[1][i] == path[2][i]):
        i += 1
 
    # Returns the last matching
    # node in the paths
    return path[1][i - 1]
 
def addEdge(a: int, b: int) -> None:
 
    tree[a].append(b)
    tree[b].append(a)
 
# Driver code
if __name__ == "__main__":
 
    n = 8
     
    # Number of nodes
    addEdge(1, 2)
    addEdge(1, 3)
    addEdge(2, 4)
    addEdge(2, 5)
    addEdge(2, 6)
    addEdge(3, 7)
    addEdge(3, 8)
 
    print("LCA(4, 7) = {}".format(LCA(4, 7)))
    print("LCA(4, 6) = {}".format(LCA(4, 6)))
 
# This code is contributed by sanjeev2552


C#




/* C# Program to find LCA of n1 and n2 using one DFS on
the Tree */
using System;
using System.Collections.Generic;
 
class GFG
{
 
    // Maximum number of nodes is 100000 and nodes are
    // numbered from 1 to 100000
    static readonly int MAXN = 100001;
 
    static List<int>[] tree = new List<int>[MAXN];
    static int[,] path = new int[3, MAXN]; // storing root to node path
    static bool flag;
 
    // storing the path from root to node
    static void dfs(int cur, int prev, int pathNumber, int ptr, int node)
    {
        for (int i = 0; i < tree[cur].Count; i++)
        {
            if (tree[cur][i] != prev && !flag)
            {
                // pushing current node into the path
                path[pathNumber,ptr] = tree[cur][i];
                if (tree[cur][i] == node)
                {
                    // node found
                    flag = true;
 
                    // terminating the path
                    path[pathNumber, ptr + 1] = -1;
                    return;
                }
                dfs(tree[cur][i], cur, pathNumber, ptr + 1, node);
            }
        }
    }
 
    // This Function compares the path from root to 'a' & root
    // to 'b' and returns LCA of a and b. Time Complexity : O(n)
    static int LCA(int a, int b)
    {
        // trivial case
        if (a == b)
            return a;
 
        // setting root to be first element in path
        path[1, 0] = path[2, 0] = 1;
 
        // calculating path from root to a
        flag = false;
        dfs(1, 0, 1, 1, a);
 
        // calculating path from root to b
        flag = false;
        dfs(1, 0, 2, 1, b);
 
        // runs till path 1 & path 2 matches
        int i = 0;
        while (i < MAXN && path[1, i] == path[2, i])
            i++;
 
        // returns the last matching node in the paths
        return path[1, i - 1];
    }
 
    static void addEdge(int a, int b)
    {
        tree[a].Add(b);
        tree[b].Add(a);
    }
 
    // Driver code
    public static void Main(String[] args)
    {
        for (int i = 0; i < MAXN; i++)
            tree[i] = new List<int>();
 
        // Number of nodes
        addEdge(1, 2);
        addEdge(1, 3);
        addEdge(2, 4);
        addEdge(2, 5);
        addEdge(2, 6);
        addEdge(3, 7);
        addEdge(3, 8);
 
        Console.Write("LCA(4, 7) = " + LCA(4, 7) + "\n");
        Console.Write("LCA(4, 6) = " + LCA(4, 6) + "\n");
    }
}
 
// This code is contributed by Rajput-Ji


Javascript




<script>
/* Program to find LCA of n1 and n2 using one DFS on
the Tree */
 
// Maximum number of nodes is 100000 and nodes are
    // numbered from 1 to 100000
let MAXN = 100001;
 
let tree = new Array(MAXN);
 
let path = new Array(3);
 
for(let i = 0; i < 3; i++)
{
    path[i] = new Array(MAXN);
    for(let j = 0; j < MAXN; j++)
    {
        path[i][j] = 0;
    }
}
 
let flag;
 
/// storing the path from root to node
function dfs(cur,prev,pathNumber,ptr,node)
{
    for (let i = 0; i < tree[cur].length; i++)
        {
            if (tree[cur][i] != prev && !flag)
            {
                // pushing current node into the path
                path[pathNumber][ptr] = tree[cur][i];
                if (tree[cur][i] == node)
                {
                    // node found
                    flag = true;
  
                    // terminating the path
                    path[pathNumber][ptr + 1] = -1;
                    return;
                }
                dfs(tree[cur][i], cur, pathNumber, ptr + 1, node);
            }
        }
}
 
// This Function compares the path from root to 'a' & root
    // to 'b' and returns LCA of a and b. Time Complexity : O(n)
function LCA(a,b)
{
    // trivial case
        if (a == b)
            return a;
  
        // setting root to be first element in path
        path[1][0] = path[2][0] = 1;
  
        // calculating path from root to a
        flag = false;
        dfs(1, 0, 1, 1, a);
  
        // calculating path from root to b
        flag = false;
        dfs(1, 0, 2, 1, b);
  
        // runs till path 1 & path 2 matches
        let i = 0;
        while (i < MAXN && path[1][i] == path[2][i])
            i++;
  
        // returns the last matching node in the paths
        return path[1][i - 1];
}
 
function addEdge(a,b)
{
    tree[a].push(b);
    tree[b].push(a);
}
 
// Driver code
for (let i = 0; i < MAXN; i++)
    tree[i] = [];
 
// Number of nodes
addEdge(1, 2);
addEdge(1, 3);
addEdge(2, 4);
addEdge(2, 5);
addEdge(2, 6);
addEdge(3, 7);
addEdge(3, 8);
 
document.write("LCA(4, 7) = " + LCA(4, 7) + "<br>");
document.write("LCA(4, 6) = " + LCA(4, 6) + "<br>");
 
// This code is contributed by rag2127
</script>


Output

LCA(4, 7) = 1
LCA(4, 6) = 2

Sparse Matrix Approach (O(nlogn) pre-processing, O(log n) – query) 

Pre-computation:

Here we store the 2^i th parent for every node, where 0 <= i < LEVEL, here “LEVEL” is a constant integer that tells the maximum number of 2^i th ancestor possible. 

Therefore, we assume the worst case to see what is the value of the constant LEVEL. In our worst case every node in our tree will have at max 1 parent and 1 child or we can say it simply reduces to a linked list. 

So, in this case LEVEL = ceil ( log(number of nodes) ). 

We also pre-compute the height for each node using one dfs in O(n) time. 

int n             // number of nodes
int parent[MAXN][LEVEL] // all initialized to -1 

parent[node][0] : contains the 2^0th(first) 
parent of all the nodes pre-computed using DFS

// Sparse matrix Approach
for node -> 1 to n :        
  for i-> 1 to LEVEL :
    if ( parent[node][i-1] != -1 ) :
      parent[node][i]  =  
         parent[ parent[node][i-1] ][i-1]

Now , as we see the above dynamic programming code runs two nested loop that runs over their complete range respectively. 
Hence, it can be easily be inferred that its asymptotic Time Complexity is O(number of nodes * LEVEL) ~ O(n*LEVEL) ~ O(nlogn).

Return LCA(u,v): 

  1. First Step is to bring both the nodes at the same height. As we have already pre-computed the heights for each node. We first calculate the difference in the heights of u and v (let’s say v >=u). Now we need the node ‘v’ to jump h nodes above. This can be easily done in O(log h) time ( where h is the difference in the heights of u and v) as we have already stored the 2^i parent for each node. This process is exactly same as calculating x^y in O(log y) time. (See the code for better understanding).
  2. Now both u and v nodes are at same height. Therefore now once again we will use 2^i jumping strategy to reach the first Common Parent of u and v. 
Pseudo-code:
For i->  LEVEL to 0 :
      If parent[u][i] != parent[v][i] :
           u = parent[u][i]
           v = parent[v][i]

Implementation of the above algorithm is given below: 

C++




// Sparse Matrix DP approach to find LCA of two nodes
#include <bits/stdc++.h>
using namespace std;
#define MAXN 100000
#define level 18
 
vector <int> tree[MAXN];
int depth[MAXN];
int parent[MAXN][level];
 
// pre-compute the depth for each node and their
// first parent(2^0th parent)
// time complexity : O(n)
void dfs(int cur, int prev)
{
    depth[cur] = depth[prev] + 1;
    parent[cur][0] = prev;
    for (int i=0; i<tree[cur].size(); i++)
    {
        if (tree[cur][i] != prev)
            dfs(tree[cur][i], cur);
    }
}
 
// Dynamic Programming Sparse Matrix Approach
// populating 2^i parent for each node
// Time complexity : O(nlogn)
void precomputeSparseMatrix(int n)
{
    for (int i=1; i<level; i++)
    {
        for (int node = 1; node <= n; node++)
        {
            if (parent[node][i-1] != -1)
                parent[node][i] =
                    parent[parent[node][i-1]][i-1];
        }
    }
}
 
// Returning the LCA of u and v
// Time complexity : O(log n)
int lca(int u, int v)
{
    if (depth[v] < depth[u])
        swap(u, v);
 
    int diff = depth[v] - depth[u];
 
    // Step 1 of the pseudocode
    for (int i=0; i<level; i++)
        if ((diff>>i)&1)
            v = parent[v][i];
 
    // now depth[u] == depth[v]
    if (u == v)
        return u;
 
    // Step 2 of the pseudocode
    for (int i=level-1; i>=0; i--)
        if (parent[u][i] != parent[v][i])
        {
            u = parent[u][i];
            v = parent[v][i];
        }
 
    return parent[u][0];
}
 
void addEdge(int u,int v)
{
    tree[u].push_back(v);
    tree[v].push_back(u);
}
 
// driver function
int main()
{
    memset(parent,-1,sizeof(parent));
    int n = 8;
    addEdge(1,2);
    addEdge(1,3);
    addEdge(2,4);
    addEdge(2,5);
    addEdge(2,6);
    addEdge(3,7);
    addEdge(3,8);
    depth[0] = 0;
 
    // running dfs and precalculating depth
    // of each node.
    dfs(1,0);
 
    // Precomputing the 2^i th ancestor for every node
    precomputeSparseMatrix(n);
 
    // calling the LCA function
    cout << "LCA(4, 7) = " << lca(4,7) << endl;
    cout << "LCA(4, 6) = " << lca(4,6) << endl;
    return 0;
}


Java




// Sparse Matrix DP approach to find LCA of two nodes
import java.util.*;
 
class GFG
{
    static final int MAXN = 100000;
    static final int level = 18;
   
      @SuppressWarnings("unchecked")
    static Vector<Integer>[] tree = new Vector[MAXN];
    static int[] depth = new int[MAXN];
    static int[][] parent = new int[MAXN][level];
 
    // pre-compute the depth for each node and their
    // first parent(2^0th parent)
    // time complexity : O(n)
    static void dfs(int cur, int prev)
    {
        depth[cur] = depth[prev] + 1;
        parent[cur][0] = prev;
        for (int i = 0; i < tree[cur].size(); i++)
        {
            if (tree[cur].get(i) != prev)
                dfs(tree[cur].get(i), cur);
        }
    }
 
    // Dynamic Programming Sparse Matrix Approach
    // populating 2^i parent for each node
    // Time complexity : O(nlogn)
    static void precomputeSparseMatrix(int n)
    {
        for (int i = 1; i < level; i++)
        {
            for (int node = 1; node <= n; node++)
            {
                if (parent[node][i - 1] != -1)
                    parent[node][i] = parent[parent[node][i - 1]][i - 1];
            }
        }
    }
 
    // Returning the LCA of u and v
    // Time complexity : O(log n)
    static int lca(int u, int v)
    {
        if (depth[v] < depth[u])
        {
            u = u + v;
            v = u - v;
            u = u - v;
        }
 
        int diff = depth[v] - depth[u];
 
        // Step 1 of the pseudocode
        for (int i = 0; i < level; i++)
            if (((diff >> i) & 1) == 1)
                v = parent[v][i];
 
        // now depth[u] == depth[v]
        if (u == v)
            return u;
 
        // Step 2 of the pseudocode
        for (int i = level - 1; i >= 0; i--)
            if (parent[u][i] != parent[v][i])
            {
                u = parent[u][i];
                v = parent[v][i];
            }
 
        return parent[u][0];
    }
 
    static void addEdge(int u, int v)
    {
        tree[u].add(v);
        tree[v].add(u);
    }
 
    static void memset(int value)
    {
        for (int i = 0; i < MAXN; i++)
        {
            for (int j = 0; j < level; j++)
            {
                parent[i][j] = -1;
            }
        }
    }
 
    // driver function
    public static void main(String[] args)
    {
        memset(-1);
        for (int i = 0; i < MAXN; i++)
            tree[i] = new Vector<Integer>();
        int n = 8;
        addEdge(1, 2);
        addEdge(1, 3);
        addEdge(2, 4);
        addEdge(2, 5);
        addEdge(2, 6);
        addEdge(3, 7);
        addEdge(3, 8);
        depth[0] = 0;
 
        // running dfs and precalculating depth
        // of each node.
        dfs(1, 0);
 
        // Precomputing the 2^i th ancestor for every node
        precomputeSparseMatrix(n);
 
        // calling the LCA function
        System.out.print("LCA(4, 7) = " + lca(4, 7) + "\n");
        System.out.print("LCA(4, 6) = " + lca(4, 6) + "\n");
    }
}
 
// This code is contributed by 29AjayKumar


Python3




# Sparse Matrix DP approach to find LCA of two nodes
MAXN = 100000
level = 18
tree = [[] for _ in range(MAXN)]
depth = [0 for _ in range(MAXN)]
parent = [[-1 for _ in range(level)] for _ in range(MAXN)]
 
# pre-compute the depth for each node and their
# first parent(2^0th parent)
# time complexity : O(n)
 
 
def dfs(cur, prev):
    depth[cur] = depth[prev] + 1
    parent[cur][0] = prev
    for i in range(len(tree[cur])):
        if tree[cur][i] != prev:
            dfs(tree[cur][i], cur)
 
# Dynamic Programming Sparse Matrix Approach
# populating 2^i parent for each node
# Time complexity : O(nlogn)
 
 
def precompute_sparse_matrix(n):
    for i in range(1, level):
        for node in range(1, n+1):
            if parent[node][i-1] != -1:
                parent[node][i] = parent[parent[node][i-1]][i-1]
 
# Returning the LCA of u and v
# Time complexity : O(log n)
 
 
def lca(u, v):
    if depth[v] < depth[u]:
        u, v = v, u
    diff = depth[v] - depth[u]
    # Step 1 of the pseudocode
    for i in range(level):
        if (diff >> i) & 1 == 1:
            v = parent[v][i]
    # now depth[u] == depth[v]
    if u == v:
        return u
    # Step 2 of the pseudocode
    for i in range(level-1, -1, -1):
        if parent[u][i] != parent[v][i]:
            u = parent[u][i]
            v = parent[v][i]
    return parent[u][0]
 
 
def add_edge(u, v):
    tree[u].append(v)
    tree[v].append(u)
 
 
def memset(value):
    for i in range(MAXN):
        for j in range(level):
            parent[i][j] = -1
 
 
# Driver function
 
n = 8
add_edge(1, 2)
add_edge(1, 3)
add_edge(2, 4)
add_edge(2, 5)
add_edge(2, 6)
add_edge(3, 7)
add_edge(3, 8)
depth[0] = 0
# running dfs and precalculating depth
# of each node.
dfs(1, 0)
# Precomputing the 2^i th ancestor for every node
precompute_sparse_matrix(n)
# calling the LCA function
print("LCA(4, 7) = ", lca(4, 7))
print("LCA(4, 6) = ", lca(4, 6))


C#




// Sparse Matrix DP approach to find LCA of two nodes
using System;
using System.Collections.Generic;
 
class GFG
{
    static readonly int MAXN = 100000;
    static readonly int level = 18;
 
    static List<int>[] tree = new List<int>[MAXN];
    static int[] depth = new int[MAXN];
    static int[,] parent = new int[MAXN, level];
 
    // pre-compute the depth for each node and their
    // first parent(2^0th parent)
    // time complexity : O(n)
    static void dfs(int cur, int prev)
    {
        depth[cur] = depth[prev] + 1;
        parent[cur,0] = prev;
        for (int i = 0; i < tree[cur].Count; i++)
        {
            if (tree[cur][i] != prev)
                dfs(tree[cur][i], cur);
        }
    }
 
    // Dynamic Programming Sparse Matrix Approach
    // populating 2^i parent for each node
    // Time complexity : O(nlogn)
    static void precomputeSparseMatrix(int n)
    {
        for (int i = 1; i < level; i++)
        {
            for (int node = 1; node <= n; node++)
            {
                if (parent[node, i - 1] != -1)
                    parent[node, i] = parent[parent[node, i - 1], i - 1];
            }
        }
    }
 
    // Returning the LCA of u and v
    // Time complexity : O(log n)
    static int lca(int u, int v)
    {
        if (depth[v] < depth[u])
        {
            u = u + v;
            v = u - v;
            u = u - v;
        }
 
        int diff = depth[v] - depth[u];
 
        // Step 1 of the pseudocode
        for (int i = 0; i < level; i++)
            if (((diff >> i) & 1) == 1)
                v = parent[v, i];
 
        // now depth[u] == depth[v]
        if (u == v)
            return u;
 
        // Step 2 of the pseudocode
        for (int i = level - 1; i >= 0; i--)
            if (parent[u, i] != parent[v, i])
            {
                u = parent[u, i];
                v = parent[v, i];
            }
 
        return parent[u, 0];
    }
 
    static void addEdge(int u, int v)
    {
        tree[u].Add(v);
        tree[v].Add(u);
    }
 
    static void memset(int value)
    {
        for (int i = 0; i < MAXN; i++)
        {
            for (int j = 0; j < level; j++)
            {
                parent[i, j] = -1;
            }
        }
    }
 
    // Driver function
    public static void Main(String[] args)
    {
        memset(-1);
        for (int i = 0; i < MAXN; i++)
            tree[i] = new List<int>();
        int n = 8;
        addEdge(1, 2);
        addEdge(1, 3);
        addEdge(2, 4);
        addEdge(2, 5);
        addEdge(2, 6);
        addEdge(3, 7);
        addEdge(3, 8);
        depth[0] = 0;
 
        // running dfs and precalculating depth
        // of each node.
        dfs(1, 0);
 
        // Precomputing the 2^i th ancestor for every node
        precomputeSparseMatrix(n);
 
        // calling the LCA function
        Console.Write("LCA(4, 7) = " + lca(4, 7) + "\n");
        Console.Write("LCA(4, 6) = " + lca(4, 6) + "\n");
    }
}
 
// This code is contributed by PrinciRaj1992


Javascript




<script>
 
// Sparse Matrix DP approach to find LCA of two nodes
var MAXN = 100000;
var level = 18;
var tree = Array.from(Array(MAXN), ()=>Array());
var depth = Array(MAXN).fill(0);
var parent = Array.from(Array(MAXN), ()=>Array(level).fill(-1));
// pre-compute the depth for each node and their
// first parent(2^0th parent)
// time complexity : O(n)
function dfs(cur, prev)
{
    depth[cur] = depth[prev] + 1;
    parent[cur][0] = prev;
    for (var i = 0; i < tree[cur].length; i++)
    {
        if (tree[cur][i] != prev)
            dfs(tree[cur][i], cur);
    }
}
// Dynamic Programming Sparse Matrix Approach
// populating 2^i parent for each node
// Time complexity : O(nlogn)
function precomputeSparseMatrix(n)
{
    for (var i = 1; i < level; i++)
    {
        for(var node = 1; node <= n; node++)
        {
            if (parent[node][i - 1] != -1)
                parent[node][i] = parent[parent[node][i - 1]][i - 1];
        }
    }
}
// Returning the LCA of u and v
// Time complexity : O(log n)
function lca(u, v)
{
    if (depth[v] < depth[u])
    {
        u = u + v;
        v = u - v;
        u = u - v;
    }
    var diff = depth[v] - depth[u];
    // Step 1 of the pseudocode
    for (var i = 0; i < level; i++)
        if (((diff >> i) & 1) == 1)
            v = parent[v][i];
    // now depth[u] == depth[v]
    if (u == v)
        return u;
    // Step 2 of the pseudocode
    for (var i = level - 1; i >= 0; i--)
        if (parent[u][i] != parent[v][i])
        {
            u = parent[u][i];
            v = parent[v][i];
        }
    return parent[u][0];
}
function addEdge(u, v)
{
    tree[u].push(v);
    tree[v].push(u);
}
function memset(value)
{
    for (var i = 0; i < MAXN; i++)
    {
        for (var j = 0; j < level; j++)
        {
            parent[i][j] = -1;
        }
    }
}
// Driver function
memset(-1);
var n = 8;
addEdge(1, 2);
addEdge(1, 3);
addEdge(2, 4);
addEdge(2, 5);
addEdge(2, 6);
addEdge(3, 7);
addEdge(3, 8);
depth[0] = 0;
// running dfs and precalculating depth
// of each node.
dfs(1, 0);
// Precomputing the 2^i th ancestor for every node
precomputeSparseMatrix(n);
// calling the LCA function
document.write("LCA(4, 7) = " + lca(4, 7) + "<br>");
document.write("LCA(4, 6) = " + lca(4, 6) + "<br>");
 
 
</script>


Output

LCA(4, 7) = 1
LCA(4, 6) = 2

Time Complexity: 

The time complexity for answering a single LCA query will be O(logn) but the overall time complexity is dominated by precalculation of the 2^i th ( 0<=i<=level ) ancestors for each node. Hence, the overall asymptotic Time Complexity will be O(n*logn) and Space Complexity will be O(nlogn), for storing the data about the ancestors of each node.

 



Last Updated : 20 Jan, 2023
Like Article
Save Article
Previous
Next
Share your thoughts in the comments
Similar Reads