• [笔记]: LCA最近公共祖先 2017-06-01 11:38 38人阅读 评论(0) 收藏


    LCALeast Common Ancestor),顾名思义,是指在一棵树中,距离两个点最近的两者的公共节点。也就是说,在两个点通往根的道路上,肯定会有公共的节点,我们就是要求找到公共的节点中,深度尽量深的点。

    还可以表示成另一种说法,就是如果把树看成是一个图,这找到这两个点中的最短距离。

    1.dfs序+RMQLCA

    /*
    用dfs序和时间戳 
    如果求m,n的LCA 记录m,n第一次出现的时间
    在时间之内 找到一点有最小深度 此点就是lca 
    样例 
    5 4
    1 2
    1 3
    2 4
    2 5
    4 5
    */
    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    #define M 1000
    #define N 1000
    using namespace std;
    int n,m;int num=0;
    int b[M],nt[M],p[N];
    int a[N],height[N],ttime=0,first[N],minnum[N][N];
    void insert(){
    	scanf("%d%d",&n,&m);
    	for(int i=1;i<=m;i++){
    		int x,y;
    		scanf("%d%d",&x,&y);
    		num++;
    		b[num]=y;
    		nt[num]=p[x];
    		p[x]=num;
    
    		num++;
    		b[num]=x;
    		nt[num]=p[y];
    		p[y]=num;
    	}
    }
    void dfs(int x,int h){
    	++ttime;
    	a[ttime]=x;
    	height[ttime]=h;
    	if(!first[x])
    		first[x]=ttime;
    	for(int e=p[x];e;e=nt[e])
    	{
    		int k=b[e];
    		if(!first[k]) dfs(k,h+1);
    		 ++ttime;
            a[ttime]=x;
            height[ttime]=h;
    	}
    }
    void RMQ()//用RMQ求height中最小的结点  
    {//此时minnum中存的是最小的height对应的编号
    	for(int i=1;i<=ttime;i++)
    		minnum[i][0]=i;
        int t=log2(ttime);
        for(int k=1;k<=t;k++){
            for(int i=1; i<=ttime;i++)
            {
            if(height[minnum[i][k-1]]<=height[minnum[i+(1<<(k-1))][k-1]])//比较左区间最小的height对应的编号对应的height(就是最小的height)与右区间的...... 
               		minnum[i][k]=minnum[i][k-1];
            	else minnum[i][k]=minnum[i+(1<<(k-1))][k-1];//一定是1<<(k-1)不是k!!! 
            }
        }
    }
    int lca(int x,int y){
    	if(x>y) swap(x,y);
    	int k;
        int t=log2(ttime);
    	if(height[minnum[x][t-1]]<height[minnum[y-t+1][t-1]])
    		k=minnum[x][t-1];
    	else k=minnum[y-t+1][t-1];
    	//printf("k=%d
    ",k);
    	return a[k];
    }
    int main(){
    	insert();
    	dfs(1,0);
    	RMQ();
    	int x,y;
    	scanf("%d%d",&x,&y);
    	printf("%d
    ",lca(first[x],first[y]));
    	return 0;
    }

    2.倍增法求LCA(在线做法) 详细见注释

    /*
    样例:
    10 9
    1 2
    1 3
    2 4
    2 5
    3 6
    5 7
    5 8
    7 9
    7 10
    9 6
    (求9,6的lca) 
    思路 两步
    1.将深度大的点翻到与深度小的点同一高度
    2.两个点一起上翻 找lca 
    预处理fa二维数组  d一维数组 
    fa[x][y] 表示顶点x向上走2^y个顶点到达的点 d[x]数组是每个点的深度 
    */
    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    #define M 1000
    #define N 1000
    using namespace std;
    int n,m;int num=0;
    int b[M],w[M],nt[M],p[N];
    int fa[M][20],d[M];
    void insert(){
    	scanf("%d%d",&n,&m);
    	for(int i=1;i<=m;i++){
    		int x,y;
    		scanf("%d%d",&x,&y);
    		num++;
    		b[num]=y;
    		nt[num]=p[x];
    		p[x]=num;
    
    		num++;
    		b[num]=x;
    		nt[num]=p[y];
    		p[y]=num;
    	}
    }
    void dfs(int x){
    	for(int i=1;i<=20;i++){//这里的20视题而定 这里表示最大深度为2^20 
    		if(d[x]<(1<<i))break;
    		fa[x][i]=fa[fa[x][i-1]][i-1];
    	}//求fa数组的关键 和RMQ类似 
    	for(int i=p[x];i>0;i=nt[i]){
    		int v=b[i];
    		if(v==fa[x][0]) continue;//无向边 不走已经走过的父节点 
    		fa[v][0]=x;
    		d[v]=d[x]+1;//求深度=父节点深度+1 
            dfs(v);
    	}
    }
    int lca(int x,int y){
    	int h;
    	if(d[x]<d[y]) swap(x,y);
    	for(h=0;(1<<h)<=d[x];h++);
    	h--;
    	for(int i=h;i>=0;i--){
    		if(d[x]-(1<<i)>=d[y])
    			x=fa[x][i];
    	}//使a b深度相等 
    	/*
    		或者可以写成
    		int t=d[x]-d[y];
    		for(int i=0;i<=20;i++)
    		{
    			if(t&(1<<i))
    			x=fa[x][i];
    		}
    		利用位运算 20的解释同上 
    	*/
    	if(x==y) return x;//如果相等直接return 
    	for(int i=h;i>=0;i--){//找lca 从大到小找(图中就是从上往下) lca是最下面的 公共父节点 
    		if(fa[x][i]!=fa[y][i]){//关键:如果 不相等 fa[x][i]和fa[y][i]一定在lca的下面一个位置 
    			x=fa[x][i];
    			y=fa[y][i];
    		}
    	}
    	return fa[x][0];//lca 
    }	 
    int main(){
    	insert();
    	dfs(1);
    	int x,y;
    	scanf("%d%d",&x,&y);
    	printf("
    LCA=%d
    ",lca(x,y));
    	return 0;
    }
    


    3.LCA求最短距离

    /*
    样例:
    10 9
    1 2 8
    1 3 7
    2 4 5
    2 5 3
    3 6 5
    5 7 4 
    5 8 4
    7 9 3
    7 10 2
    9 6
    (求9,6的最短距离)
    思路 求m到n最短距离 
    =m到lca的距离+n到lca的距离
    =m到根的距离+n到根的距离-2*lca到根的距离 
    */
    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    #define M 1000
    #define N 1000
    using namespace std;
    int n,m;int num=0;
    int b[M],w[M],nt[M],p[N];
    int fa[M][20],d[M],val[N];
    void insert(){
    	scanf("%d%d",&n,&m);
    	for(int i=1;i<=m;i++){
    		int x,y,z;
    		scanf("%d%d%d",&x,&y,&z);
    		num++;
    		b[num]=y;
    		w[num]=z;
    		nt[num]=p[x];
    		p[x]=num;
    
    		num++;
    		b[num]=x;
    		w[num]=z;
    		nt[num]=p[y];
    		p[y]=num;
    	}
    }
    void dfs(int x){
    	for(int i=1;i<=20;i++){//这里的20视题而定 这里表示最大深度为2^20 
    		if(d[x]<(1<<i))break;
    		fa[x][i]=fa[fa[x][i-1]][i-1];//求fa数组的关键 和RMQ类似 
    	}
    	for(int i=p[x];i>0;i=nt[i]){
    		int v=b[i];
    		if(v==fa[x][0]) continue;//无向边 不走已经走过的父节点 
    		fa[v][0]=x;
    		d[v]=d[x]+1;//求深度=父节点深度+1 
    		val[v]=val[x]+w[i];//求与根结点距离 和深度类似
            dfs(v);
    	}
    }
    int lca(int x,int y){//求LCA 
    	int h;
    	if(d[x]<d[y]) swap(x,y);
    	for(h=0;(1<<h)<=d[x];h++);
    	h--;
    	for(int i=h;i>=0;i--){
    		if(d[x]-(1<<i)>=d[y])
    			x=fa[x][i];
    	}
    	if(x==y) return x;
    	for(int i=h;i>=0;i--){
    		if(fa[x][i]!=fa[y][i]){
    			x=fa[x][i];
    			y=fa[y][i];
    		}
    	}
    	return fa[x][0];
    }	 
    int main(){
    	insert();
    	dfs(1);
    	int m,n;
    	scanf("%d%d",&m,&n);
    	printf("
    LCA=%d
    ",lca(m,n));
    	printf("Distance=%d
    ",val[m]+val[n]-2*val[lca(m,n)]);
    	return 0;
    }
    



  • 相关阅读:
    CentOS7.5 搭建MyCat1.6.6
    idea快速搭建springboot项目
    MySQL存储过程中变量及循环的使用
    windows 安装 jdk1.8并配置环境变量
    CentOS7.5安装JDK1.8
    CentOS7.2安装MySql5.7并开启远程连接授权
    PHP高级工程师面试
    每日英语
    静态化
    php分页实例及其原理
  • 原文地址:https://www.cnblogs.com/xljxlj/p/7183633.html
Copyright © 2020-2023  润新知