LCA专解

先愉快地来一波百度百科:

LCA(Least Common Ancestors),即最近公共祖先,是指在有根树中,找出某两个结点u和v最近的公共祖先。

对于有根树T的两个结点u、v,最近公共祖先LCA(T,u,v)表示一个结点x,满足x是u、v的祖先且x的深度尽可能大。

另一种理解方式是把T理解为一个无向无环图,而LCA(T,u,v)即u到v的最短路上深度最小的点。(莫名的槽点:教练写的叫 lowest common ancestors)...

今日客官们且听我讲一波优雅的暴力算法(好吧大家都知道的倍增)。。。

首先我们想简单的暴力怎么做:

假设我们求13与11的LCA,自然容易想到将13向上跳一格与11同一深度,然后两个点一起向上跳,跳到了6,这个就是我们要求的lca。

接下来我们思考怎么优化算法:学过树状数组的同学们都知道,树状数组就是以倍增来进行高效的区间储存的;倍增,顾名思义,

就是幂运算,常用2^n(因为2^n可以精确到1,2^0=1);倍增算法可以大大节省我们跳跃所花费的时间,它的效率是logn级别(n为结点总数)的;

接下来我们来看看具体的步骤:

part 1:

加边:邻接表不多解释了。。。

void add(int u,int v){
	next[++cnt]=first[u];first[u]=cnt;to[cnt]=v;
	next[++cnt]=first[v];first[v]=cnt;to[cnt]=u;
}

 

 part 2:

预处理:我们需要预处理每一个结点的深度以及从该结点开始跳i步所到达的位置

f[u][i]表示从u结点开始向上跳2^i次所到达的结点;

dep[u]表示x的深度

dep[u]=dep[father]+1:当前结点深度等于其父亲结点深度+1....

f[u][i+1]=f[f[u][i]][i]:这一步比较玄妙,向上2^i次,就是向上2^(i-1)后再向上2^(i-1),所以就可以递推下去了。。。

ps:一般循环开到2^19次够了int 极限

void deal_first(int u,int father){
	dep[u]=dep[father]+1;
	for(int i=0;i<=19;i++){
		f[u][i+1]=f[f[u][i]][i];
	}
	for(int e=first[u];e;e=next[e]){//处理与当前u相连的下一个点 
		int v=to[e];
		if(v==father)//已经处理过 
			continue;
		f[v][0]=u;//v结点是u的son,v跳2^0到u 
		deal_first(v,u);//递归下去 
	}
}

 

part 3:

lca:因为此时已经预处理好了所有所需数据,直接调用就好;

inline int lca(int x,int y){
	if(dep[x]=0;i--){//跳到同一深度 
		if(dep[f[x][i]]>=dep[y])//只有当x的深度仍大与等于y的深度时,对x进行跳跃 
			x=f[x][i];
	}
	if(x==y)
			return x;//当 x,y在树的同一分支,x,y的公共祖先是其中一个 
	for(int i=19;i>=0;i--){//两者一起进行跳跃 
		if(f[x][i]!=f[y][i])
			x=f[x][i],y=f[y][i];
	}
	return f[x][0];// 最后一定跳到最近公共祖先的儿子上 
}

写的已经很详细了。。。

那么上例题吧

https://blog.csdn.net/fxy1699/article/details/87897429

谢谢各位看官~~

持续更新其他求LCA算法~~

你可能感兴趣的:(LCA,lca)