The idea is to traverse upward from each node towards the root node while keeping track of the visited node(s). Since upward traversal requires each node to know its parent, we do an initial depth-first search to find the parent of each node.
Note : The parent node in a tree can have more than 2 children nodes.
Algorithm
Example
Consider finding the lowest common ancestor of nodes 11 and 8 in the below tree.
Upward traversal from node 8 towards the root yields the path [ 8 3 0 ], all the nodes in the path are marked visited.
Upward traversal from node 11 towards the root takes the path [ 11 7 3 0 ].
When the upward traveral from node ‘11’ encounters a visited node ‘3’ in the path, the algorithm returns ‘3’ as the lowest common ancestor as it was visited during an earlier traversal and hence must be the lowest common ancestor.
Similary consider finding the LCA of node 0 and node 2.
Upward traversal path of node 2 is [ 2 0 ]. Nodes 2, 0 are marked visited.
Upward traversal path of node 0 is [ 0 ].
When a visited node 0 is encountered during upward traversal of node 0, it evidently becomes the lowest common ancestor of node 0 and node 2.
Data structure for storing tree: Adjacency List
Data structure for storing parent nodes: list / vector
Time complexity : O ( N ), where N is the number of nodes in the tree. If the tree is unbalanced i.e in the worst case if every node of the tree has only one node, the tree becomes linear with height = N. If we have to find the lowest common ancestor “M” times, the time complexity becomes N*M, as for every query (M) we have to traverse the height of the tree.
Program for finding the Lowest Common Ancestor (LCA) in a binary / multinode tree using upward traversal
#include<iostream>
#include<list>
#include<vector>
using namespace std;
class Tree{
private:
list<int> *adjlist;
vector<int> parentlist;
vector<bool> visited;
public:
Tree() {
}
Tree (int nodes) {
adjlist = new list<int> [nodes];
parentlist.resize(nodes);
visited.resize(nodes);
}
~Tree() {
delete [] adjlist;
}
void AddEdge (int src, int dst) {
adjlist[src].push_back(dst);
adjlist[dst].push_back(src);
}
// Get parent of every node
void GetParent (int node, int parent){
for(auto& v : adjlist[node]) {
if (v != parent) {
parentlist[v] = node;
GetParent(v, node);
}
}
}
int GetLCA (int root_node, int node_a, int node_b){
int lca = root_node;
// Traverse from node_a upto the root node;
while (true) {
visited[node_a] = true;
if (node_a == root_node) {
break;
}
node_a = parentlist[node_a];
}
/* Travese from node_b up to the root node. If along the path a visited parent is found,
it is the LCA of (node_a, node_b) */
while (true){
if (visited[node_b]) {
lca = node_b;
break;
}
node_b = parentlist[node_b];
}
return lca;
}
};
int main(){
Tree t(15);
// AddEdge (Parent, Child)
t.AddEdge(0,1);
t.AddEdge(0,2);
t.AddEdge(0,3);
t.AddEdge(1,4);
t.AddEdge(1,5);
t.AddEdge(1,6);
t.AddEdge(3,7);
t.AddEdge(3,8);
t.AddEdge(4,9);
t.AddEdge(7,11);
t.AddEdge(9,10);
t.AddEdge(11,12);
t.AddEdge(11,13);
t.AddEdge(11,14);
// Root node does not have any parent; so set it to -1
t.GetParent(0, -1);
int root_node = 0;
cout << "LCA of (10, 13) : " << t.GetLCA(root_node, 10, 13) << endl;
cout << "LCA of (4, 6) : " << t.GetLCA(root_node, 4, 6) << endl;
cout << "LCA of (11, 8) : " << t.GetLCA(root_node, 11, 8) << endl;
cout << "LCA of (0, 2) : " << t.GetLCA(root_node, 0, 2) << endl;
cout << "LCA of (6, 0) : " << t.GetLCA(root_node, 6, 0) << endl;
cout << "LCA of (14, 8) : " << t.GetLCA(root_node, 14, 8) << endl;
return 0;
}
Output
LCA of (10, 13) : 0
LCA of (4, 6) : 1
LCA of (11, 8) : 3
LCA of (0, 2) : 0
LCA of (6, 0) : 0
LCA of (14, 8) : 3
import java.util.ArrayList;
import java.util.List;
class Tree {
private List<List<Integer>> adjlist;
private List<Integer> parentlist;
private List<Boolean> visited;
Tree (int nodes) {
adjlist = new ArrayList<>(nodes);
parentlist = new ArrayList<>(nodes);
visited = new ArrayList<>(nodes);
for (int i = 0; i < nodes; i++) {
adjlist.add(new ArrayList<>());
parentlist.add(-1); //initialize the parrent array
visited.add(false); //initialize the visited array
}
}
void AddEdge (int src, int dst) {
adjlist.get(src).add(dst);
adjlist.get(dst).add(src);
}
//Get Parent of every node
void GetParent (int node, int parent) {
for (int v : adjlist.get(node)) {
if (v != parent) {
parentlist.set(v, node);
GetParent(v, node);
}
}
}
int GetLCA (int root_node, int node_a, int node_b) {
int lca = root_node;
//Traverse from node_a upto the root_node
while(true) {
visited.set(node_a, true);
if (node_a == root_node) {
break;
}
node_a = parentlist.get(node_a);
}
/* Travese from node_b up to the root node. If along the path a visited parent is found,
it is the LCA of (node_a, node_b) */
while (true) {
if (visited.get(node_b)) {
lca = node_b;
break;
}
node_b = parentlist.get(node_b);
}
return lca;
}
public static void main (String args[]) {
Tree t = new Tree(15);
t.AddEdge(0,1);
t.AddEdge(0,2);
t.AddEdge(0,3);
t.AddEdge(1,4);
t.AddEdge(1,5);
t.AddEdge(1,6);
t.AddEdge(3,7);
t.AddEdge(3,8);
t.AddEdge(4,9);
t.AddEdge(7,11);
t.AddEdge(9,10);
t.AddEdge(11,12);
t.AddEdge(11,13);
t.AddEdge(11,14);
// Root node does not have any parent; so set it to -1
t.GetParent(0, -1);
int root_node = 0;
System.out.println("LCA of (10, 13) : " + t.GetLCA(root_node, 10, 13));
System.out.println("LCA of (4, 6) : " + t.GetLCA(root_node, 4, 6));
System.out.println("LCA of (11, 8) : " + t.GetLCA(root_node, 11, 8));
System.out.println("LCA of (0, 2) : " + t.GetLCA(root_node, 0, 2));
System.out.println("LCA of (6, 0) : " + t.GetLCA(root_node, 6, 0));
System.out.println("LCA of (14, 8) : " + t.GetLCA(root_node, 14, 8));
}
}
Output
LCA of (10, 13) : 0
LCA of (4, 6) : 1
LCA of (11, 8) : 3
LCA of (0, 2) : 0
LCA of (6, 0) : 0
LCA of (14, 8) : 3