Lowest Common Ancestor - Tarjan's off-line algorithm¶
We have a tree
In this article we will solve the problem off-line, i.e. we assume that all queries are known in advance, and we therefore answer the queries in any order we like.
The following algorithm allows to answer all
Algorithm¶
The algorithm is named after Robert Tarjan, who discovered it in 1979 and also made many other contributions to the Disjoint Set Union data structure, which will be heavily used in this algorithm.
The algorithm answers all queries with one DFS traversal of the tree.
Namely a query
So let's assume we are currently at node
Note that
We only need to learn to efficiently maintain all these sets.
For this purpose we apply the data structure DSU.
To be able to apply Union by rank, we store the real representative (the value on the path between ancestor
.
Let's discuss the implementation of the DFS.
Let's assume we are currently visiting the node ancestor[v] = v
.
As usual we process all children of union_sets
and the following assignment ancestor[find_set(v)] = v
(this is necessary, because union_sets
might change the representative of the set).
Finally after processing all children we can answer all queries of the form ancestor[find_set(u)]
.
It is easy to see that a query will only be answered once.
Let's us determine the time complexity of this algorithm.
Firstly we have union_sets
which happen find_set
for every query, which gives
Implementation¶
Here is an implementation of this algorithm. The implementation of DSU has been not included, as it can be used without any modifications.
vector<vector<int>> adj;
vector<vector<int>> queries;
vector<int> ancestor;
vector<bool> visited;
void dfs(int v)
{
visited[v] = true;
ancestor[v] = v;
for (int u : adj[v]) {
if (!visited[u]) {
dfs(u);
union_sets(v, u);
ancestor[find_set(v)] = v;
}
}
for (int other_node : queries[v]) {
if (visited[other_node])
cout << "LCA of " << v << " and " << other_node
<< " is " << ancestor[find_set(other_node)] << ".\n";
}
}
void compute_LCAs() {
// initialize n, adj and DSU
// for (each query (u, v)) {
// queries[u].push_back(v);
// queries[v].push_back(u);
// }
ancestor.resize(n);
visited.assign(n, false);
dfs(0);
}