tarjan求lca
Tarjan 是一种 DFS 的思想。我们需要从根结点去遍历这棵树。
当遍历到某一个结点(称之为 x) 时,你有以下几点需要做的。
1将当前结点标记为已经访问。
2递归遍历所有它的子节点(称之为 y),并在递归执行完后用并查集合并 x 和 y。
3遍历与当前节点有查询关系的结点(称之为 z)(即是需要查询 LCA 的另一些结点),如果 z 已经访问,那么 x 与 z 的 LCA 就是 getfa(z)(这个是并查集中的查找函数),输出或者记录下来就可以了。
这是伪代码
void tarjan(int x){
//在本代码段中,s[i]为第i个子节点 , t[i]为第i个和当前节点有查询关系的结点。
vis[x]=1;//标记已经访问,vis是记录是否已访问的数组
for (i=1;i<=子节点数;i++){//枚举子节点 (递归并合并)
tarjan(s[i]);
marge(x,s[i]);//并查集合并
}
for (i=1;i<=有查询关系的结点数;i++){
if (vis[t[i]]){
cout<<x<<"和"<<t[i]<<"的LCA是"<<getfa(t[i])<<endl;//如果t[i]已经访问了输出(getfa是并查集查找函数)
}
}
}
倍增求lca
用邻接表存储一棵树, 并用from[]数组记录各结点的父节点, 其中没有父节点的就是root.
parents[u][]数组存储的是u结点的祖先结点.
如parents[u][0], 是u结点的2⁰祖先结点, 即1祖先, 也即父节点. 在输入过程中可以直接得到.
parents[u][1], 是u结点的2¹祖先结点,即2祖先, 也即父亲的父亲
parents[u][2], 是u结点的2²祖先结点, 即4祖先, 也即(父亲的父亲)的(父亲的父亲), 也就是爷爷的爷爷.
#include <iostream>
#include <algorithm>
#include <cstring>
#include <queue>
#include <vector>
using namespace std;
const int maxn = 10005;
int parents[maxn][20], depth[maxn];
int n, from[maxn], root = -1;
vector<int> G[maxn];
void init()
{
memset(parents, -1, sizeof(parents));
memset(from, -1, sizeof(from));
memset(depth, -1, sizeof(depth));
}
void getData()//获取图的邻接表存储,存储第一个父节点
{
cin >> n;
int u, v;
for (int i = 1; i < n; ++i) {
cin >> u >> v;
G[u].push_back(v);
parents[v][0] = u;
from[v] = 1;
}
for (int i = 1; i <= n; ++i) {
if (from[i] == -1) root = i;//找到根
}
}
void getDepth_dfs(int u)//dfs求深度
{
int len = G[u].size();
for (int i = 0; i < len; ++i) {
int v = G[u][i];
depth[v] = depth[u] + 1;
getDepth_dfs(v);
}
}
void getDepth_bfs(int u)//bfs求深度
{
queue<int> Q;
Q.push(u);
while (!Q.empty()) {
int v = Q.front();
Q.pop();
for (int i = 0; i < G[v].size(); ++i) {
depth[G[v][i]] = depth[v] + 1;
Q.push(G[v][i]);
}
}
}
void getParents()//求祖先,核心
{
for (int up = 1; (1 << up) <= n; ++up) {
for (int i = 1; i <= n; ++i) {
parents[i][up] = parents[parents[i][up - 1]][up - 1];
}
}
}
int Lca(int u, int v)
{
if (depth[u] < depth[v]) swap(u, v);//让u的深度大
int i = -1, j;
while ((1 << (i + 1)) <= depth[u]) ++i;
for (j = i; j >= 0; --j) {
if (depth[u] - (1 << j) >= depth[v]) {
u = parents[u][j];
}
}//调整uv到同一深度
if (u == v) return u;
//一起往上走
for (j = i; j >= 0; --j) {
if (parents[u][j] != parents[v][j]) {
u = parents[u][j];
v = parents[v][j];
}
}
return parents[u][0];//注意返回的是父节点
}
void questions()
{
int q, u, v;
cin >> q;
for (int i = 0; i < q; ++i) {
cin >> u >> v;
int ans = Lca(u, v);
cout << ans << endl;
//cout << u << " 和 " << v << " 的最近公共祖先(LCA)是: " << ans << endl;
}
}
int main()
{
init();
getData();
depth[root] = 1;
getDepth_dfs(root);
//getDepth_bfs(root);
getParents();
questions();
}
/*
9
1 2
1 3
1 4
2 5
2 6
3 7
6 8
7 9
5
1 3
5 6
8 9
8 4
5 8
*/