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.

Pre-requisites:
- DFS
- Basic DP knowledge (This and this)
- 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++
#include<bits/stdc++.h>
using namespace std;
#define MAXN 100001
vector < int > tree[MAXN];
int path[3][MAXN];
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)
{
path[pathNumber][ptr] = tree[cur][i];
if (tree[cur][i] == node)
{
flag = true ;
path[pathNumber][ptr+1] = -1;
return ;
}
dfs(tree[cur][i], cur, pathNumber, ptr+1,
node, flag);
}
}
}
int LCA( int a, int b)
{
if (a == b)
return a;
path[1][0] = path[2][0] = 1;
bool flag = false ;
dfs(1, 0, 1, 1, a, flag);
flag = false ;
dfs(1, 0, 2, 1, b, flag);
int i = 0;
while (path[1][i] == path[2][i])
i++;
return path[1][i-1];
}
void addEdge( int a, int b)
{
tree[a].push_back(b);
tree[b].push_back(a);
}
int main()
{
int n = 8;
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
import java.util.*;
class GFG
{
static final int MAXN = 100001 ;
static Vector<Integer>[] tree = new Vector[MAXN];
static int [][] path = new int [ 3 ][MAXN];
static boolean flag;
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)
{
path[pathNumber][ptr] = tree[cur].get(i);
if (tree[cur].get(i) == node)
{
flag = true ;
path[pathNumber][ptr + 1 ] = - 1 ;
return ;
}
dfs(tree[cur].get(i), cur, pathNumber, ptr + 1 , node);
}
}
}
static int LCA( int a, int b)
{
if (a == b)
return a;
path[ 1 ][ 0 ] = path[ 2 ][ 0 ] = 1 ;
flag = false ;
dfs( 1 , 0 , 1 , 1 , a);
flag = false ;
dfs( 1 , 0 , 2 , 1 , b);
int i = 0 ;
while (i < MAXN && path[ 1 ][i] == path[ 2 ][i])
i++;
return path[ 1 ][i - 1 ];
}
static void addEdge( int a, int b)
{
tree[a].add(b);
tree[b].add(a);
}
public static void main(String[] args)
{
for ( int i = 0 ; i < MAXN; i++)
tree[i] = new Vector<Integer>();
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" );
}
}
|
Python3
MAXN = 100001
tree = [ 0 ] * MAXN
for i in range (MAXN):
tree[i] = []
path = [ 0 ] * 3
for i in range ( 3 ):
path[i] = [ 0 ] * MAXN
flag = False
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):
path[pathNumber][ptr] = tree[cur][i]
if (tree[cur][i] = = node):
flag = True
path[pathNumber][ptr + 1 ] = - 1
return
dfs(tree[cur][i], cur, pathNumber,
ptr + 1 , node)
def LCA(a: int , b: int ) - > int :
global flag
if (a = = b):
return a
path[ 1 ][ 0 ] = path[ 2 ][ 0 ] = 1
flag = False
dfs( 1 , 0 , 1 , 1 , a)
flag = False
dfs( 1 , 0 , 2 , 1 , b)
i = 0
while (path[ 1 ][i] = = path[ 2 ][i]):
i + = 1
return path[ 1 ][i - 1 ]
def addEdge(a: int , b: int ) - > None :
tree[a].append(b)
tree[b].append(a)
if __name__ = = "__main__" :
n = 8
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 )))
|
C#
using System;
using System.Collections.Generic;
class GFG
{
static readonly int MAXN = 100001;
static List< int >[] tree = new List< int >[MAXN];
static int [,] path = new int [3, MAXN];
static bool flag;
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)
{
path[pathNumber,ptr] = tree[cur][i];
if (tree[cur][i] == node)
{
flag = true ;
path[pathNumber, ptr + 1] = -1;
return ;
}
dfs(tree[cur][i], cur, pathNumber, ptr + 1, node);
}
}
}
static int LCA( int a, int b)
{
if (a == b)
return a;
path[1, 0] = path[2, 0] = 1;
flag = false ;
dfs(1, 0, 1, 1, a);
flag = false ;
dfs(1, 0, 2, 1, b);
int i = 0;
while (i < MAXN && path[1, i] == path[2, i])
i++;
return path[1, i - 1];
}
static void addEdge( int a, int b)
{
tree[a].Add(b);
tree[b].Add(a);
}
public static void Main(String[] args)
{
for ( int i = 0; i < MAXN; i++)
tree[i] = new List< int >();
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" );
}
}
|
Javascript
<script>
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;
function dfs(cur,prev,pathNumber,ptr,node)
{
for (let i = 0; i < tree[cur].length; i++)
{
if (tree[cur][i] != prev && !flag)
{
path[pathNumber][ptr] = tree[cur][i];
if (tree[cur][i] == node)
{
flag = true ;
path[pathNumber][ptr + 1] = -1;
return ;
}
dfs(tree[cur][i], cur, pathNumber, ptr + 1, node);
}
}
}
function LCA(a,b)
{
if (a == b)
return a;
path[1][0] = path[2][0] = 1;
flag = false ;
dfs(1, 0, 1, 1, a);
flag = false ;
dfs(1, 0, 2, 1, b);
let i = 0;
while (i < MAXN && path[1][i] == path[2][i])
i++;
return path[1][i - 1];
}
function addEdge(a,b)
{
tree[a].push(b);
tree[b].push(a);
}
for (let i = 0; i < MAXN; i++)
tree[i] = [];
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>" );
</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):
- 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).
- 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++
#include <bits/stdc++.h>
using namespace std;
#define MAXN 100000
#define level 18
vector < int > tree[MAXN];
int depth[MAXN];
int parent[MAXN][level];
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);
}
}
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];
}
}
}
int lca( int u, int v)
{
if (depth[v] < depth[u])
swap(u, v);
int diff = depth[v] - depth[u];
for ( int i=0; i<level; i++)
if ((diff>>i)&1)
v = parent[v][i];
if (u == v)
return u;
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);
}
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;
dfs(1,0);
precomputeSparseMatrix(n);
cout << "LCA(4, 7) = " << lca(4,7) << endl;
cout << "LCA(4, 6) = " << lca(4,6) << endl;
return 0;
}
|
Java
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];
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);
}
}
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 ];
}
}
}
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];
for ( int i = 0 ; i < level; i++)
if (((diff >> i) & 1 ) == 1 )
v = parent[v][i];
if (u == v)
return u;
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 ;
}
}
}
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 ;
dfs( 1 , 0 );
precomputeSparseMatrix(n);
System.out.print( "LCA(4, 7) = " + lca( 4 , 7 ) + "\n" );
System.out.print( "LCA(4, 6) = " + lca( 4 , 6 ) + "\n" );
}
}
|
Python3
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)]
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)
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 ]
def lca(u, v):
if depth[v] < depth[u]:
u, v = v, u
diff = depth[v] - depth[u]
for i in range (level):
if (diff >> i) & 1 = = 1 :
v = parent[v][i]
if u = = v:
return u
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
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
dfs( 1 , 0 )
precompute_sparse_matrix(n)
print ( "LCA(4, 7) = " , lca( 4 , 7 ))
print ( "LCA(4, 6) = " , lca( 4 , 6 ))
|
C#
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];
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);
}
}
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];
}
}
}
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];
for ( int i = 0; i < level; i++)
if (((diff >> i) & 1) == 1)
v = parent[v, i];
if (u == v)
return u;
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;
}
}
}
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;
dfs(1, 0);
precomputeSparseMatrix(n);
Console.Write( "LCA(4, 7) = " + lca(4, 7) + "\n" );
Console.Write( "LCA(4, 6) = " + lca(4, 6) + "\n" );
}
}
|
Javascript
<script>
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));
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);
}
}
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];
}
}
}
function lca(u, v)
{
if (depth[v] < depth[u])
{
u = u + v;
v = u - v;
u = u - v;
}
var diff = depth[v] - depth[u];
for ( var i = 0; i < level; i++)
if (((diff >> i) & 1) == 1)
v = parent[v][i];
if (u == v)
return u;
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;
}
}
}
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;
dfs(1, 0);
precomputeSparseMatrix(n);
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.
If you like GeeksforGeeks and would like to contribute, you can also write an article using write.geeksforgeeks.org or mail your article to review-team@geeksforgeeks.org. See your article appearing on the GeeksforGeeks main page and help other Geeks.
Feeling lost in the world of random DSA topics, wasting time without progress? It's time for a change! Join our DSA course, where we'll guide you on an exciting journey to master DSA efficiently and on schedule.
Ready to dive in? Explore our Free Demo Content and join our DSA course, trusted by over 100,000 geeks!
Last Updated :
20 Jan, 2023
Like Article
Save Article