We have seen various methods with different Time Complexities to calculate LCA in n-ary tree:-
Method 1 : Naive Method ( by calculating root to node path) | O(n) per query
Method 2 :Using Sqrt Decomposition | O(sqrt H)
Method 3 : Using Sparse Matrix DP approach | O(logn)
Lets study another method which has faster query time than all the above methods. So, our aim will be to calculate LCA in constant time ~ O(1). Let’s see how we can achieve it.
Method 4 : Using Range Minimum Query
We have discussed LCA and RMQ for binary tree. Here we discuss LCA problem to RMQ problem conversion for n-ary tree.
Pre-requisites:- LCA in Binary Tree using RMQ RMQ using sparse table
Key Concept : In this method, we will be reducing our LCA problem to RMQ(Range Minimum Query) problem over a static array. Once, we do that then we will relate the Range minimum queries to the required LCA queries.
The first step will be to decompose the tree into a flat linear array. To do this we can apply the Euler walk . The Euler walk will give the pre-order traversal of the graph. So we will perform a Euler Walk on the tree and store the nodes in an array as we visit them. This process reduces the tree data-structure to a simple linear array.
Consider the below tree and the euler walk over it :-

Now lets think in general terms : Consider any two nodes on the tree. There will be exactly one path connecting both the nodes and the node that has the smallest depth value in the path will be the LCA of the two given nodes.
Now take any two distinct node say u and v in the Euler walk array. Now all the elements in the path from u to v will lie in between the index of nodes u and v in the Euler walk array. Therefore, we just need to calculate the node with the minimum depth between the index of node u and node v in the euler array.
For this we will maintain another array that will contain the depth of all the nodes corresponding to their position in the Euler walk array so that we can Apply our RMQ algorithm on it.
Given below is the euler walk array parallel to its depth track array.

Example :- Consider two nodes node 6 and node 7 in the euler array. To calculate the LCA of node 6 and node 7 we look the smallest depth value for all the nodes in between node 6 and node 7 .
Therefore, node 1 has the smallest depth value = 0 and hence, it is the LCA for node 6 and node 7.

Implementation:-
We will be maintaining three arrays 1)Euler Path
2)Depth array
3)First Appearance Index
Euler Path and Depth array are the same as described above
First Appearance Index FAI[] : The First Appearance index Array will store the index for the first position of every node in the Euler Path array. FAI[i] = First appearance of ith node in Euler Walk array.
The Implementation for the above method is given below:-
C++
// C++ program to demonstrate LCA of n-ary tree// in constant time.#include "bits/stdc++.h"using namespace std;#define sz 101vector < int > adj[sz]; // stores the treevector < int > euler; // tracks the eulerwalkvector < int > depthArr; // depth for each node corresponding // to eulerwalkint FAI[sz]; // stores first appearence index of every nodeint level[sz]; // stores depth for all nodes in the treeint ptr; // pointer to euler walkint dp[sz][18]; // sparse tableint logn[sz]; // stores log valuesint p2[20]; // stores power of 2void buildSparseTable(int n){ // initializing sparse table memset(dp,-1,sizeof(dp)); // filling base case values for (int i=1; i<n; i++) dp[i-1][0] = (depthArr[i]>depthArr[i-1])?i-1:i; // dp to fill sparse table for (int l=1; l<15; l++) for (int i=0; i<n; i++) if (dp[i][l-1]!=-1 and dp[i+p2[l-1]][l-1]!=-1) dp[i][l] = (depthArr[dp[i][l-1]]>depthArr[dp[i+p2[l-1]][l-1]])? dp[i+p2[l-1]][l-1] : dp[i][l-1]; else break;}int query(int l,int r){ int d = r-l; int dx = logn[d]; if (l==r) return l; if (depthArr[dp[l][dx]] > depthArr[dp[r-p2[dx]][dx]]) return dp[r-p2[dx]][dx]; else return dp[l][dx];}void preprocess(){ // memorizing powers of 2 p2[0] = 1; for (int i=1; i<18; i++) p2[i] = p2[i-1]*2; // memorizing all log(n) values int val = 1,ptr=0; for (int i=1; i<sz; i++) { logn[i] = ptr-1; if (val==i) { val*=2; logn[i] = ptr; ptr++; } }}/** * Euler Walk ( preorder traversal) * converting tree to linear depthArray * Time Complexity : O(n) * */void dfs(int cur,int prev,int dep){ // marking FAI for cur node if (FAI[cur]==-1) FAI[cur] = ptr; level[cur] = dep; // pushing root to euler walk euler.push_back(cur); // incrementing euler walk pointer ptr++; for (auto x:adj[cur]) { if (x != prev) { dfs(x,cur,dep+1); // pushing cur again in backtrack // of euler walk euler.push_back(cur); // increment euler walk pointer ptr++; } }}// Create Level depthArray corresponding// to the Euler walk Arrayvoid makeArr(){ for (auto x : euler) depthArr.push_back(level[x]);}int LCA(int u,int v){ // trival case if (u==v) return u; if (FAI[u] > FAI[v]) swap(u,v); // doing RMQ in the required range return euler[query(FAI[u], FAI[v])];}void addEdge(int u,int v){ adj[u].push_back(v); adj[v].push_back(u);}int main(int argc, char const *argv[]){ // constructing the described tree int numberOfNodes = 8; addEdge(1,2); addEdge(1,3); addEdge(2,4); addEdge(2,5); addEdge(2,6); addEdge(3,7); addEdge(3,8); // performing required precalculations preprocess(); // doing the Euler walk ptr = 0; memset(FAI,-1,sizeof(FAI)); dfs(1,0,0); // creating depthArray corresponding to euler[] makeArr(); // building sparse table buildSparseTable(depthArr.size()); cout << "LCA(6,7) : " << LCA(6,7) << "\n"; cout << "LCA(6,4) : " << LCA(6,4) << "\n"; return 0;} |
Java
// Java program to demonstrate LCA of n-ary// tree in constant time.import java.util.ArrayList;import java.util.Arrays;class GFG{static int sz = 101;@SuppressWarnings("unchecked")// Stores the treestatic ArrayList<Integer>[] adj = new ArrayList[sz]; // Tracks the eulerwalkstatic ArrayList<Integer> euler = new ArrayList<>(); // Depth for each node correspondingstatic ArrayList<Integer> depthArr = new ArrayList<>(); // to eulerwalk// Stores first appearence index of every nodestatic int[] FAI = new int[sz]; // Stores depth for all nodes in the treestatic int[] level = new int[sz]; // Pointer to euler walkstatic int ptr;// Sparse tablestatic int[][] dp = new int[sz][18];// Stores log valuesstatic int[] logn = new int[sz];// Stores power of 2static int[] p2 = new int[20];static void buildSparseTable(int n){ // Initializing sparse table for(int i = 0; i < sz; i++) { for(int j = 0; j < 18; j++) { dp[i][j] = -1; } } // Filling base case values for(int i = 1; i < n; i++) dp[i - 1][0] = (depthArr.get(i) > depthArr.get(i - 1)) ? i - 1 : i; // dp to fill sparse table for(int l = 1; l < 15; l++) for(int i = 0; i < n; i++) if (dp[i][l - 1] != -1 && dp[i + p2[l - 1]][l - 1] != -1) dp[i][l] = (depthArr.get(dp[i][l - 1]) > depthArr.get( dp[i + p2[l - 1]][l - 1])) ? dp[i + p2[l - 1]][l - 1] : dp[i][l - 1]; else break;}static int query(int l, int r) { int d = r - l; int dx = logn[d]; if (l == r) return l; if (depthArr.get(dp[l][dx]) > depthArr.get(dp[r - p2[dx]][dx])) return dp[r - p2[dx]][dx]; else return dp[l][dx];}static void preprocess() { // Memorizing powers of 2 p2[0] = 1; for(int i = 1; i < 18; i++) p2[i] = p2[i - 1] * 2; // Memorizing all log(n) values int val = 1, ptr = 0; for(int i = 1; i < sz; i++) { logn[i] = ptr - 1; if (val == i) { val *= 2; logn[i] = ptr; ptr++; } }}// Euler Walk ( preorder traversal) converting// tree to linear depthArray // Time Complexity : O(n)static void dfs(int cur, int prev, int dep){ // Marking FAI for cur node if (FAI[cur] == -1) FAI[cur] = ptr; level[cur] = dep; // Pushing root to euler walk euler.add(cur); // Incrementing euler walk pointer ptr++; for(Integer x : adj[cur]) { if (x != prev) { dfs(x, cur, dep + 1); // Pushing cur again in backtrack // of euler walk euler.add(cur); // Increment euler walk pointer ptr++; } }}// Create Level depthArray corresponding// to the Euler walk Arraystatic void makeArr(){ for(Integer x : euler) depthArr.add(level[x]);}static int LCA(int u, int v) { // Trival case if (u == v) return u; if (FAI[u] > FAI[v]) { int temp = u; u = v; v = temp; } // Doing RMQ in the required range return euler.get(query(FAI[u], FAI[v]));}static void addEdge(int u, int v){ adj[u].add(v); adj[v].add(u);}// Driver codepublic static void main(String[] args){ for(int i = 0; i < sz; i++) { adj[i] = new ArrayList<>(); } // Constructing the described tree int numberOfNodes = 8; addEdge(1, 2); addEdge(1, 3); addEdge(2, 4); addEdge(2, 5); addEdge(2, 6); addEdge(3, 7); addEdge(3, 8); // Performing required precalculations preprocess(); // Doing the Euler walk ptr = 0; Arrays.fill(FAI, -1); dfs(1, 0, 0); // Creating depthArray corresponding to euler[] makeArr(); // Building sparse table buildSparseTable(depthArr.size()); System.out.println("LCA(6,7) : " + LCA(6, 7)); System.out.println("LCA(6,4) : " + LCA(6, 4));}}// This code is contributed by sanjeev2552 |
Output:
LCA(6,7) : 1 LCA(6,4) : 2
Note : We are precalculating all the required power of 2’s and also precalculating the all the required log values to ensure constant time complexity per query. Else if we did log calculation for every query operation our Time complexity would have not been constant.
Time Complexity: The Conversion process from LCA to RMQ is done by Euler Walk that takes O(n) time.
Pre-processing for the sparse table in RMQ takes O(nlogn) time and answering each Query is a Constant time process. Therefore, overall Time Complexity is O(nlogn) – preprocessing and O(1) for each query.
This article is contributed by Nitish Kumar. If you like GeeksforGeeks and would like to contribute, you can also write an article using contribute.geeksforgeeks.org or mail your article to contribute@geeksforgeeks.org. See your article appearing on the GeeksforGeeks main page and help other Geeks.
Please write comments if you find anything incorrect, or you want to share more information about the topic discussed above.
Attention reader! Don’t stop learning now. Get hold of all the important DSA concepts with the DSA Self Paced Course at a student-friendly price and become industry ready.
Recommended Posts:
- Query to find the maximum and minimum weight between two nodes in the given tree using LCA.
- Find LCA in Binary Tree using RMQ
- LCA in a tree using Binary Lifting Technique
- Sqrt (or Square Root) Decomposition | Set 2 (LCA of Tree in O(sqrt(height)) time)
- LCA for general or n-ary trees (Sparse Matrix DP approach )
- Segment Tree | Set 2 (Range Minimum Query)
- Iterative Segment Tree (Range Maximum Query with Node Update)
- Iterative Segment Tree (Range Minimum Query)
- Proto Van Emde Boas Tree | Set 3 | Insertion and isMember Query
- Proto Van Emde Boas Tree | Set 6 | Query : Successor and Predecessor
- Query for ancestor-descendant relationship in a tree
- Segment Tree | Set 2 (Range Maximum Query with Node Update)
- Connect nodes at same level using constant extra space
- Design a data structure that supports insert, delete, search and getRandom in constant time
- Constant time range add operation on an array
- Complexity of different operations in Binary tree, Binary Search Tree and AVL tree
- Maximum sub-tree sum in a Binary Tree such that the sub-tree is also a BST
- Convert a Generic Tree(N-array Tree) to Binary Tree
- Range query for Largest Sum Contiguous Subarray
- Range sum query using Sparse Table

