Tarjan算法
前向星型
#include<stdio.h>
#define N 500000
//数组形式的链表
struct Edge{
int to,next,w;
}edge[N],q[N]; //q数组存储查询的两点
int num_edge,num_q,head[N],qhead[N];//M表示边的序号
int pre[N],vis[N];//pre[i]存储i的父节点
//建立链表 添加边
void add_edge(int from,int to)
{
edge[++num_edge].next=head[from];//以from结点出发的上一条边在数组中的编号
edge[num_edge].to=to;
head[from]=num_edge;
}
void add_q(int from,int to)
{
q[++num_q].next=qhead[from];
q[num_q].to=to;
qhead[from]=num_q;
}
int find(int x)//寻找x的父亲结点
{
while(pre[x]!=x)
x=pre[x];
return x;
}
int dfs(int x)
{
pre[x]=x;
vis[x]=1;
//遍历与x相连的结点
for(int k=head[x];k;k=edge[k].next){
int v=edge[k].to;
if(!vis[v]){//如果未被搜索
dfs(v);//以该结点为根节点,继续搜索
pre[v]=x;//把v的父亲结点设为x
}
}
//搜索包含结点x的所有查询
for(int k=qhead[x];k;k=q[k].next){
int v=q[k].to;
if(vis[v]){//如果另一结点已被搜索过
q[k].w=find(v);//把另一节点的祖先设为这两个节点的最近公共祖先
if(k%2)//由于将每一组查询变为两组,所以2n-1和2n的结果是一样的
q[k+1].w=q[k].w;
else
q[k-1].w=q[k].w;
}
}
}
int main()
{
int n,m,p,x,y,i;
scanf("%d%d%d",&n,&m,&p);//输入结点数,查询数和根结点
for(i=1;i<n;i++){
scanf("%d%d",&x,&y);
add_edge(x,y);
add_edge(y,x);
}
for(int i=1;i<=m;i++){
scanf("%d%d",&x,&y);//输入每次查询
add_q(x,y);
add_q(y,x);
}
dfs(p);//进入以p为根节点的树的深搜
for(i=1;i<=m;i++)
printf("%d ",q[i*2].w);//两组结果一样,输出一组即可
return 0;
}
暴力求LCA
//暴力求LCA
#include<iostream>
#include<cstring>
#include<vector>
using namespace std;
const int N=10010;
vector<int>g[N];//邻接表
int depth[N],f[N],in[N];//depth存储各结点的深度,f存储各结点的父节点
void dfs(int rt,int fa)//分别表示本结点和本结点的父亲结点
{
depth[rt]=depth[fa]+1;
f[rt]=fa;
for(int i=0;i<g[rt].size();i++)
dfs(g[rt][i],rt);
}
int LCA(int a,int b)
{
if(depth[a]>depth[b])
swap(a,b);
while(depth[b]>depth[a])
b=f[b];
while(a!=b)
a=f[a],b=f[b];
return a;
}
int main()
{
int t,n,a,b;
scanf("%d",&t);
while(t--){
scanf("%d",&n);
for(int i=0;i<n;i++)
g[i].clear();
memset(in,0,sizeof(in));
for(int i=1;i<n;i++){
scanf("%d%d",&a,&b);
g[a].push_back(b);
in[b]++;//b的入度加一
}
depth[0]=-1;
int rt=0;
for(int i=1;i<=n&&rt==0;i++)//寻找树的根结点
if(in[i]==0)
rt=i;
dfs(rt,0);
scanf("%d%d",&a,&b);
printf("%d\n",LCA(a,b));
}
return 0;
}
倍增算法求LCA
void dfs(int u)
{
for(int i=head[u];i;i=edge[i].next){
int to=edge[i].to;
if(to==p[u][0])
continue;
d[to]=d[u]+1;
dist[to]=dist[u]+edge[i].w;
p[to][0]=u;//p[i][0]存i的父节点
dfs(to);
}
}
//i的2^j祖先是i的2^(j-1)祖先的2^(j-1)祖先
void init()
{
for(int j=1;(1<<j)<=n;j++)
for(int i=1;i<=n;i++)
p[i][j]=p[p[i][j-1]][j-1];
}
void lca(int a,int b)
{
if(d[a]>d[b])
swap(a,b);//b在下面
int f=d[b]-d[a];//f是高度差
for(int i=0;(1<<i)<=f;i++)
if((1<<i)&&f)//(1<<i)&f找到f化为2进制后1的位置,移动到相应的位置
b=p[b][i];//比如f=5(101),先移动2^0祖先,然后再移动2^2祖先
if(a!=b){
for(int i=(int)log2(N);i>=0;i--)
if(p[a][i]!=p[b][i]){//从最大祖先开始,判断a,b祖先,是否相同
a=p[a][i];//如不相同,a b同时向上移动2^j
b=p[b][i];
}
a=p[a][0];//这时a的father就是LCA
}
return a;
}