acm算法之lca最近公共祖先

//lca(求最小公共祖先) 此处考虑使用最简单的被增法解决最近公共祖先问题
#include<stdio.h>
#include<string.h>
struct edge
{
	int to,next;
}edge[1000];
int cnt,head[1000],lg[1000];
void add(int x,int y)//链式前向星存边,即存图 
{
	edge[++cnt].to=y;
	edge[cnt].next=head[x];
	head[x]=cnt;
}
int deep[1000],fa[1000][21];//用deep数组存储每个点的深度,而用fa数组存储每个数,从i出发走2^j步之后到达的地方 
void dfs(int now,int father)
{
	int i;
	fa[now][0]=father;//从now出发,走2^0步,即走1步,就到达的是其父亲节点 
	deep[now]=deep[father]+1;
	for(i=1;(1<<i)<=deep[now];i++)//1<<i表示把1化为二进制,然后向左移动i位,即表示2^i.同时应注意到,fa数组存的是从now开始跳2^j层后的点位置,那么,你跳的层数一定是小于当前点的深度的,所以才有中间那句判断条件 
	{
		fa[now][i]=fa[fa[now][i-1]][i-1];//因为2^j可以表示为2^j-1+2^j-1,也就是两个相加的形式,因为最初那个fa[now][0]是赋了值的,就可以往后推,得到后面数组元素的值 
	}
	for(i=head[now];i!=0;i=edge[i].next)//链式前向星向后摸索,因为初始化的时候赋值head[i]为0,所以判断结束的时候以0为标准 
	{
		if(edge[i].to!=father)//跳过父亲节点,不然就回不去了 
		dfs(edge[i].to,now);
	}
} 
void swap(int *x,int *y)
{
	int temp;
	temp=*x;
	*x=*y;
	*y=temp;
	
} 
int lca(int x,int y)
{
	int k;
	if(deep[x]<deep[y])//避免分情况讨论,所以交换,让x的深度永远大于y的深度 
	swap(&x,&y);
	while(deep[x]>deep[y])//将x和y提到同一高度 
	{
		x=fa[x][lg[deep[x]-deep[y]-1]];
	}
		if(x==y)//跳到一个点后,两者相遇,即可判断该点为最小祖先节点 
		return x;
		else
		{
		for(k=lg[deep[x]-1];k>=0;k--)
		{
			if(fa[x][k]!=fa[y][k])
			{
				y=fa[y][k];
				x=fa[x][k];
			}
		}
		return fa[x][0];//2^0表示为1,即其上一个节点,即其父亲节点 
	
    }
}
int main(void)
{
	int i,j,t,n,m,s,x,y,a,b;
	scanf("%d %d %d",&n,&m,&s);
	for(i=1;i<=n;i++)
	{
		scanf("%d %d",&x,&y);
		add(x,y);
		add(y,x);//注意此处是双向存入,也就意味着,既以x为起点,以y为终点;也以y为起点,以x为终点。但是这里的双向存入应该理解为有两个表,并不意味着两个表合在一起 
	}
	dfs(s,0);//s表示的是根节点的值,跟节点没有父亲节点,所以传入其父亲节点实参为0 
	for(i=1;i<=n;i++)
	{
		lg[i]=lg[i-1]+(1<<lg[i-1]==i);//存储每个数字的lg值,因为lg1=0.所以要加1; 
		
	}
	while(m--)
	{
		scanf("%d %d",&a,&b);//查询a,b的最小公共祖先 
		printf("%d\n",lca(a,b));
	}
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值