LCA问题的RMQ解法解析


LCA问题是指最近公共祖先问题,RMQ问题是只区间最小值问题,我们可以将LCA问题转化为RMQ问题,然后利用RMQ的解法来解决LCA问题。有关RMQ问题的详解可以参考我的博客,有关于RMQ问题的详解。

本博客重点讲如何将LCA问题转化为RMQ问题。

当我们深度遍历树时,我们没遇到一个未访问过的节点就将其存入到数组中,同时记录下来的还有它的深度;当我们完成了对

每一个节点u的所有分支访问后,会回溯到该节点u,我们也将回溯时遇到的节点存入数组中,同时记录下来的还有它的深度。这样我们就能够用数组清楚的记录下来深度遍历时访问节点的完整路径流程。另外我们还用一个数组记录好每个节点第一次访问到的序,便于我们后续操作。

下图为该用例树的访问先后次序(包括回溯时重复的)

LCA问题的RMQ解法解析_第1张图片


下图中,其中i表示次序,node[i]表示先后访问的节点(包括回溯时的),depth[i]表示第i个访问的节点的深度(包括回溯时访问的)

下图中i表示节点,R[i]表示相应的节点第一次被访问的次序

我们可以发现,对于任意两个节点u、v,我们根据R[u]和R[v]找到其第一次出现的次序fu、fv,可以发现,u和v的最近祖depth[fu]到depth[fv]中深度最小的那个值对应的节点(我们假设fu

举个例子,如上图中,对于节点5和节点10,二者对应的R[5]和R[10]分别为5和14,然后我们找到depth[5]和depth[14]之间最小的深度是1,期下标值为6,然后我们根据这个下标,找到node[6]的值为3,那么它俩的最近公共祖先就是节点3。

其C++代码实现如下:

#include
#include
#include
using namespace std;
vector tree[105];                        //假设有105个节点的树
int R[105];                                   //用于深度遍历时每个节点第一次出现的次序
int depth[10005];                             //记录深度遍历时经过的节点的深度的值,一个节点课重复记录
int node[10005];                              //记录深度遍历时先后经过的节点,可重复记录
int count=1;                                  //记录好次序,count从1开始
int ns;                                       //树中节点的个数

void inputTree()						      //输入树
{
	cin>>ns;								  //树的顶点数
	for(int i=1;i<=ns;i++)					  //初始化树,顶点编号从0开始
		tree[i].clear();

	for(i=1;i>x>>y;							  //x->y有一条边
		tree[x].push_back(y); 
	}
}


/*
深度遍历为上述几个数组初始化,为RMQ提供数据
*/
void dfs(int u,int deep){
	node[count]=u;                            //记录好次序
	depth[count]=deep;

	if(!R[u])                                 //如果这个节点还未出现过
		R[u]=count;                           //则记录第一次出现的顺序
	
	count++;

	for(int i=0;i0){
		res*=2;
		i--;
	}
	return res;
}

//计算完整的ST表
void calST(){
	int logs=(int)(log(n)/log(2));              //求出最多向上递归的次数

	for(int j=1;j<=logs;j++){                   //因为j=0的情况在initST中已经算了
		for(int i=0;iST[i+tmp][j-1]?ST[i+tmp][j-1]:ST[i][j-1];//二者当中取相对小的
			else
				ST[i][j]=ST[i][j-1];            //只剩下前半段,则只用前半段
		}
	}
}

/********************************ST算法实现结束*********************************/

void init(){
	memset(R,0,sizeof(R));
	dfs(1,0);

	n=count;
	initST();
	calST();
}
/*
求节点u和节点v的最近祖先
*/
int RMQ(int u,int v){
	u=R[u];                                     //得到第一次出现的次序
	v=R[v];

	if(u>v){                                    //保证u小
		int tmp=u;
		u=v;
		v=tmp;
	}

	int logs=(int)(log(v-u+1)/log(2));          //区间长度最大2的多少次幂

	int res=ST[u][logs]>ST[v-getTwo(logs)+1][logs]?ST[v-getTwo(logs)+1][logs]:ST[u][logs];

	for(int i=u;i>m;

	for(int i=0;i>u>>v;
		cout<


你可能感兴趣的:(C++,算法导论)