最近公共祖先 LCA

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];
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

//倍增算法求LCA
#include<iostream>
#include<cstring>
#include<vector>

using namespace std;
const int N=10010;
vector<int>g[N];//邻接表
int depth[N],f[N][20],in[N];//depth存储各结点的深度,f存储各结点的父节点 

void dfs(int rt,int fa)//分别表示本结点和本结点的父亲结点 
{
	depth[rt]=depth[fa]+1;
	f[rt][0]=fa;
	for(int i=1;i<20;i++)
	  f[rt][i]=f[f[rt][i-1]][i-1];
	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);
	for(int i=19;i>=0;i--)
	  if(depth[b]-(1<<i)>=depth[a])
	    b=f[b][i];
	if(a==b)
	  return a;
	for(int i=19;i>=0;i--)
	  if(f[b][i]!=f[a][i])
	    a=f[a][i],b=f[b][i];
	return f[b][0];
}

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;
}

题目集锦

 POJ1330 Nearest Common Ancestors【LCA】

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值