最近公共祖先问题

介绍了一种将树结构转化为数组的方法,并利用该数组高效地查询两个节点的最近公共祖先(LCA)。通过深度优先搜索遍历整棵树,记录每个节点进出的时间戳及深度,进而快速定位并计算出LCA。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在树中寻找两个结点的最近公共祖先

hihoCoder给出的在线解法:将树转化为数组

“从树的根节点开始进行深度优先搜索,每次经过某一个点——无论是从它的父亲节点进入这个点,还是从它的儿子节点返回这个点,都按顺序记录下来。这样就把一棵树转换成了一个数组。而找到树上两个节点的最近公共祖先,无非就是找到这两个节点最后一次出现在数组中的位置所囊括的一段区间中深度最小的那个点。转换出的数组的长度其实就是边数的2倍而已,也是O(n)的级别。

#include <iostream>
#include <map>
#include <stack>
#include <string>
#include <stdio.h>
using namespace std;


int N=0;
int size = 0;
multimap<string,string> m ;
string *a;
int *depthA;
int preCalculate( int index );
string findMin(int begin, int end);
string query( string name1, string name2 );
void printMap();
void printArray();




int main(){
scanf("%d",&N);


string rootFather = "";
string father, child;
for( int i=0; i<N; i++ ){
cin>>father;
cin>>child;
pair<string,string> pa (father,child);
m.insert( pa );
if( i==0 )
rootFather = father;
}
//printMap();


size = 2*N+1;
a= new string[size];
depthA = new int[size];
a[0] = rootFather;
depthA[0] = 0;
preCalculate(0 );
//printArray();


int M=0;
scanf( "%d", &M );

for( int i=0; i<M; i++ ){
cin>>father;
cin>>child;
cout<<query( father, child )<<endl;
}
return 0;
}


int preCalculate( int index ){
string fatherName = a[index];
int depth = depthA[index];


multimap<string,string>::iterator it = m.find(fatherName);
if( it==m.end() ){
return index;
}


pair<multimap<string,string>::iterator,multimap<string,string>::iterator> p = m.equal_range(fatherName);
int nextIndex = index+1;
for( it=p.first;it!=p.second;it++){
a[nextIndex] = it->second;
depthA[nextIndex] = depth+1;
int end = preCalculate( nextIndex );
a[ ++end ] = fatherName;
depthA[ end ]= depth;
nextIndex = ++end;
}
return --nextIndex;
}


string query( string name1, string name2 ){
if( name1==name2 )
return name1;


int begin=-1, end=-1;
for( int i=0; i<size; i++ ){
if( name1==a[i] || a[i]==name2 ){
if( begin==-1 )
begin = i;
else if( a[i]==a[begin] )
begin = i;
else{
end = i;
break;
}
}
}


return findMin(begin,end);

}


string findMin(int begin, int end){
int minDepth = depthA[begin];
int minDepthIndex = begin;
for( int i=begin+1; i<=end; i++ ){
if( minDepth > depthA[i] ){
minDepth = depthA[i];
minDepthIndex = i;
}
}
return a[minDepthIndex];
}


void printMap(){
multimap<string,string>::iterator it = m.begin();
while( it!=m.end() ){
cout<<it->first<<" "<<it->second<<endl;
it++;
}


}


void printArray(){
for( int i=0; i<size; i++ ){
cout<<a[i]<<" "<<depthA[i]<<endl;
}
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值