多叉树最近公共祖先问题(LCA)

任务:设计一个算法,对于给定的树中两结点,返回它们的最近公共祖先

输入:第1行有一个正整数n,表示给定的树有n个结点。结点编号为1,2,3,...,n,编号为1的顶点是树根。接下来n行中,第i+1行描述了第i个结点的儿子情况。每行的第一个正整数k表示该结点有k个儿子,其后k个数中,每一个数表示其儿子结点的编号。当k=0时表示该结点为叶节点。

输入的第n+2行是一个正整数m,表示要计算最近公共祖先的m个结点对。接下来的m行,每行两个正整数,前两个是结点编号,第3个是它们的最近公共祖先结点编号。

 

输入样例:

12

3 2 3 4

2 5 6

0

0

2 7 8

2 9 10

0

0

0

2 11 12

0

0

5

3 11

7 12

4 8

9 12

8 10

输出样例:

3 11 1

7 12 2

4 8 1

9 12 6

8 10 2

 

不会什么高深的算法,直接暴力搜索了。其实还可以稍微优化一下,即存储路径时只需要存储一条,然后让另一个结点一直回溯,比较判断就好了。不过想到这个的时候已经写完了,也没心思去改了、、、

   

#include <iostream>
#include <vector>
using namespace std;

typedef struct{
	int parent;
	int *son;
}TreeNode;

void findPath(TreeNode* node, int x, vector<int>& path)
{
	int i;
	//	找出该结点到根结点的路径
	//	因根结点父节点已标记为0,故以此为循环结束条件 
	while( node[x].parent != 0 ){
		i = node[x].parent;
		path.push_back(i);
		x = i;	
	}
}

int find_ClosetParent(vector<int>& path_a, vector<int>& path_b)
{
//	O( path_a.size() * path_b.size() ) 
	for(int i = 0; i < path_a.size(); ++i)
		for(int k = 0; k < path_b.size(); ++k)
			if( path_a[i] == path_b[k] )
				return path_a[i];
				
	return -1;	//	若结点对有一个是根结点,则找不到	
} 

int main()
{	
	int n, m, k, pos;
	while( cin >> n )
	{
		TreeNode node[n+1];
		//	构建树 
		node[1].parent = 0;	//根结点的父亲结点下标标记为0 
		for(int i = 1; i <= n; ++i){
//			cout << "node " << i << endl;
			cin >> k;
			node[i].son = (k!=0) ? (new int[k]):nullptr;	//m为0表示没有儿子 
			for(int j = 0; j < k; ++j){
				cin >> pos;
				node[pos].parent = i;	//将该儿子结点的父节点标记为当前结点	
				node[i].son[j] = pos;	//存储该结点的儿子结点下标 
			}
		}
		//	寻找公共祖先结点
		int a, b, res;
		cin >> m;
		while( m-- ){
//			cout << "opt " << endl;
			vector<int> path_a, path_b;
			cin >> a >> b;
			//	加速优化 
			if( a==b || node[a].parent==node[b].parent ){
				res = node[a].parent;
			}else{
				findPath(node, a, path_a);
				findPath(node, b, path_b);
				res = find_ClosetParent(path_a, path_b);
			}
			cout << a << " " << b << " " << res << endl;
		}
		//	释放node数组开辟的son数组空间
		for(int i = 1; i <= n; ++i)
			delete[] node[i].son; 
		cout << "-----END-----" << endl;	
	}
	
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值