Skip to content

Commit 50fb4e1

Browse files
authored
Merge pull request xtaci#60 from Hamada14/master
Add Lowest common ancestor algorithm
2 parents e654213 + 5c50c33 commit 50fb4e1

File tree

2 files changed

+155
-0
lines changed

2 files changed

+155
-0
lines changed

include/LCA.h

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/*******************************************************************************
2+
*
3+
*
4+
* /\ | _ _ ._ o _|_ |_ ._ _ _
5+
* /--\ | (_| (_) | | |_ | | | | | _>
6+
* _|
7+
*
8+
* LCA Finding using Binary Lifting and Dynamic Programming
9+
*
10+
* Features:
11+
* 1. Answers Query about LCA of two nodes in O(log N)
12+
* where N is the total number of nodes in a tree.
13+
*
14+
* https://en.wikipedia.org/wiki/Lowest_common_ancestor
15+
* http://www.csegeek.com/csegeek/view/tutorials/algorithms/trees/tree_part12.php
16+
******************************************************************************/
17+
18+
#ifndef LCA_H
19+
#define LCA_H
20+
#include <vector>
21+
22+
class LCA
23+
{
24+
public:
25+
LCA(std::vector< std::pair<int,int> > edges);
26+
int lcaQuery(int a, int b);
27+
28+
private:
29+
int getMaxLog();
30+
void initDP();
31+
void dfs(int currentNode, int currentParent);
32+
std::vector< std::vector<int> > adjList, binaryLiftDp;
33+
std::vector<int> parent, nodeHeight;
34+
std::vector<bool> visited;
35+
int _numberOfNodes, _maxLog;
36+
};
37+
38+
#endif // LCA_H

src/lca_demo.cpp

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
#include "LCA.h"
2+
#include <cstdio>
3+
#include <vector>
4+
#include <iostream>
5+
/**
6+
*Constructor is initialized with a Adjacency List that
7+
*describe a tree and If It doesn't describe a tree it asserts failure.
8+
*/
9+
10+
LCA::LCA(std::vector< std::pair<int,int> > edges): _numberOfNodes(edges.size() + 1), _maxLog(getMaxLog())
11+
{
12+
//First we initialize the needed vectors
13+
parent.resize(_numberOfNodes);
14+
nodeHeight.resize(_numberOfNodes);
15+
visited.resize(_numberOfNodes);
16+
adjList.resize(_numberOfNodes);
17+
binaryLiftDp = std::vector< std::vector<int> >(_numberOfNodes, std::vector<int>(_maxLog));
18+
/**Construction of the Adjacency List to increase
19+
*The efficiency of the tree traversal to O(V + E).
20+
*/
21+
for(auto edge : edges){
22+
adjList[edge.first].push_back(edge.second);
23+
adjList[edge.second].push_back(edge.first);
24+
}
25+
//Initialize the Dynamic programming Vector.
26+
initDP();
27+
}
28+
29+
/**
30+
*DFS is used to find the parent and the height of each node
31+
*allowing the use of Binary Lifting.
32+
*/
33+
void LCA::dfs(int currentNode, int currentParent)
34+
{
35+
visited[currentNode] = true;
36+
parent[currentNode] = currentParent;
37+
nodeHeight[currentNode] = nodeHeight[currentParent] + 1;
38+
int adjacencySize = adjList[currentNode].size();
39+
for(int idx = 0; idx < adjacencySize; idx++){
40+
int nextNode = adjList[currentNode][idx];
41+
if(!visited[nextNode])
42+
{
43+
dfs(nextNode, currentNode);
44+
}
45+
}
46+
}
47+
48+
/**
49+
*Used to Calculate the Log to the base of two
50+
*for the number of the nodes to create the sparse table
51+
*used in binary Lifting.
52+
*/
53+
int LCA::getMaxLog(){
54+
int curValue = 1;
55+
int curLog = 1;
56+
while(curValue < _numberOfNodes) curValue *= 2, curLog++;
57+
return curLog;
58+
}
59+
60+
void LCA::initDP()
61+
{
62+
dfs(0, -1);
63+
for(int i = 0; i < _numberOfNodes; i++) binaryLiftDp[i][0] = parent[i];
64+
for(int i = 1; i <= _maxLog; i++)
65+
{
66+
for(int j = 0; j < _numberOfNodes; j++)
67+
{
68+
/**
69+
* Since the ith parent of the current node is equal to
70+
* the ith / 2 parent to the ith /2 parent of the current node
71+
* That's why the Recurrence relation is described as follow
72+
*/
73+
if(binaryLiftDp[j][i - 1] != -1)
74+
binaryLiftDp[j][i] = binaryLiftDp[binaryLiftDp[j][i - 1]][i - 1];
75+
else binaryLiftDp[j][i] = -1;
76+
}
77+
}
78+
}
79+
80+
int LCA::lcaQuery(int a, int b)
81+
{
82+
/**
83+
* First Both nodes must have same height
84+
* So we will rise the node with the deeper height up in
85+
* the tree to where they're equal.
86+
*/
87+
if(nodeHeight[a] < nodeHeight[b]) std::swap(a,b);
88+
for(int i = _maxLog; i >= 0; i--)
89+
{
90+
if(binaryLiftDp[a][i] + 1 && nodeHeight[binaryLiftDp[a][i]] >= nodeHeight[b])
91+
a = binaryLiftDp[a][i];
92+
}
93+
/**
94+
* If the node Lower is the LCA then return it.
95+
* Else keep moving both nodes up as much as they aren't the same
96+
* until it's only 1 node left which is the direct parent of both of them
97+
*/
98+
if(a == b) return a;
99+
for(int i = _maxLog; i >= 0; i--)
100+
{
101+
if(binaryLiftDp[a][i] + 1 && binaryLiftDp[a][i] - binaryLiftDp[b][i])
102+
a = binaryLiftDp[a][i], b = binaryLiftDp[b][i];
103+
}
104+
return parent[a];
105+
}
106+
107+
int main(){
108+
std::vector< std::pair<int,int> > edges;
109+
edges.push_back({0,1});
110+
edges.push_back({1,2});
111+
edges.push_back({2,3});
112+
edges.push_back({1,4});
113+
LCA* l = new LCA(v);
114+
std::cout << l->lcaQuery(0,1) << endl;
115+
std::cout << l->lcaQuery(3,4) << endl;
116+
std::cout << l->lcaQuery(3,2) << endl;
117+
}

0 commit comments

Comments
 (0)