Nearest Common Ancestors(最近公共祖先)

文章描述了一种在计算机科学中常见的数据结构问题——寻找有根树中两个不同节点的最近公共祖先。给出的解决方案利用了深度优先搜索(DFS)构建节点的祖先路径,并通过路径压缩来高效地找到最近公共祖先。程序接收树的节点信息和查询,然后返回最近公共祖先的节点编号。
摘要由CSDN通过智能技术生成

Nearest Common Ancestors(最近公共祖先)

题目描述

有根树是一个在计算机科学和工程学中众所周知的数据结构。如下图所示的例子:
  在这里插入图片描述
在上图,每个结点用1-16中的一个整数标记。结点8是这棵树的根。

一个结点x是一个结点y的祖先当且仅当结点x是在根和结点y的路径上。例如,结点4是结点16的祖先,结点10也是结点16的祖先。实际上,结点8,4和12都是结点16的祖先。记住每个结点都是它自己的祖先。结点8,4,6和7都是结点7的祖先。

一个结点x被称做结点y和z的公共祖先当且仅当x都是y和z的祖先。因此,结点8 和4是结点16和7的公共祖先。

一个结点x被称做结点y和z的最近公共祖先当且仅当x是y和z的一个公共祖先且在y和z的所有公共祖先中是最近的。所以,结点16和7的最近公共祖先是结点4,因为结点4比结点8更接近结点16和7。

在其它例子中,结点2和3的最近公共祖先结点10,结点6和13的最近公共祖先是结点8,结点4和12的最近公共祖先是结点4。在最后的一个例子中,如果y是z的一个祖先,那么y和z的最近公共祖先是y。

写一个程序找出在一棵树中两个不同结点的最近公共祖先。


输入输出格式

输入格式:

输入包括T组数据。第一行是一个整数T。
  对每组数据,第一行是一个整数N,表示这棵树的结点数,其中2<=N<=10,000。
  这些结点标记为1,2,…,N。
  接下来的N-1行包括一对整数,代表一条边,第一个数是第二个数的父亲。
  保证N个结点恰好有N-1条边。
  最后的一行包括两个不同的整数,表示要求最近公共祖先的两个不同结点

输出格式:

对每组数据输出一行,包括一个表示所求的两个不同结点的最近公共祖先的整数

输入输出样例

输入样例:

2
16
1 14
8 5
10 16
5 9
4 6
8 4
4 10
1 13
6 15
10 11
6 7
10 2
16 3
8 1
16 12
16 7
5
2 3
3 4
3 1
1 5
3 5

输出样例:

4
3

Code 代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
using namespace std;
long long n,t,i,j,x,y,m,r,d[11000],f[11000],p[50],st[11000][30];
vector<long long> e[11000];
void dfs(long long fa)
{
	long long i;
	for(i=1;i<30;i++)
		st[fa][i]=st[st[fa][i-1]][i-1];
	for(i=0;i<e[fa].size();i++)
	{
		long long son=e[fa][i];
		st[son][0]=fa;
		d[son]=d[fa]+1;
		dfs(son);
	}
	return;
}
long long lca(long long a,long long b)
{
	long long l,i;
	if(d[a]<d[b])
		swap(a,b);
	l=d[a]-d[b];
	for(i=30;i>=0;i--)
	{
		if(l>=(1<<i))
		{
			a=st[a][i];
			l-=(1<<i);
		}
	}
	if(a==b)
		return a;
	for(i=29;i>=0;i--)
	{
		if(st[a][i]!=st[b][i])
		{
			a=st[a][i];
			b=st[b][i];
		}
	}
	return st[a][0];
}
int main()
{
	scanf("%lld",&t);
	for(i=1;i<=t;i++)
	{
		scanf("%lld",&n);
		memset(st,0,sizeof(st));
		memset(d,0,sizeof(d));
		memset(e,0,sizeof(e));
		memset(f,0,sizeof(f));
		for(j=1;j<n;j++)
		{
			scanf("%lld%lld",&x,&y);
			e[x].push_back(y);
			f[y]++;
		}
		for(j=1;j<=n;j++)
		{
			if(f[j]==0)
				r=j;
		}
		d[r]=1;
		st[r][0]=r;
		dfs(r);
		scanf("%lld%lld",&x,&y);
		m=lca(x,y);
		printf("%lld\n",m);
	}
	return  0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值