zoukankan      html  css  js  c++  java
  • 最近公共祖先(LCA)

    算法一:朴素法

    采用两点逐渐向根移动的方法,求出LCA。

    具体步骤:
    1.求出每个节点的深度
    2.询问两个节点是否重合,若重合,则LCA已经求出。否则,选择两个点中深度较大一个,移动它的父亲。

    时间复杂度(O(n)),查询(q)次的话,复杂度(O(qn))

    优点:简单,该算法允许树动态改变。

    代码如下:

    
    struct EDGE{int to,nxt;}e[MAXN];
    void addedge(int u,int v){e[++cnt].to=v; e[cnt].nxt=adj[u]; adj[u]=cnt;}
    //建树的过程
    //同时求出每个节点的深度,与每个节点的父亲
    void dfs(int u,int father)
    {
    	fat[u]=father; deep[u]=deep[father]+1;
    	for(int i=adj[u];i;i=e[i].nxt)
    	{
    		int v=e[i].to;
    		if(v!=father) dfs(v,u);
    	}
    	return ;
    }
    //相当于模拟移动
    int LCA(int u,int v)
    {
    	while(u!=v)
    	{
    		if(deep[u]>=deep[v]) u=fat[u];
    		else v=fat[v];
    	}
    	return u;
    }
    int main()
    {
    	scanf("%d%d%d",&n,&m,&s);
    	for(int i=1;i<=n-1;++i)
    	{
    		int u,v;scanf("%d%d",&u,&v);
    		addedge(u,v); addedge(v,u);
    	}
    	dfs(s,s);
    	for(int i=1;i<=m;++i)
    	{
    		int a,b; scanf("%d%d",&a,&b);
    		printf("%d
    ",LCA(a,b));
    	}
    	return 0;
    }
    

    算法二 倍增LCA

    第一步:初始化(建树)

    求出倍增数组 (anc)。(和一开始建树的思路一样)

    其中,MAXLOG是最小的满足(2^x leq MAXDEEP)(最深的节点深度)的(x)

    (anc[i][j])(i)节点向上走(2^j)后能走到的点。

    我们规定根节点的父亲就是它自己,这样根节点往上走还是根节点。

    对于(anc) 而言,有如下的递推关系:

    ( anc[i][j]=left{egin{matrix} anc[i][j] & j=0 \ anc[anc[i][j-1]][j-1]& j> 1 end{matrix} ight. )

    解释:

    第一个,(j=0)就是根节点。

    第二个,从(anc)的意义出发:节点(i)向上走(2^{j-1})步后,再走(2^{j-1})步,加起来就是(2^j)次步。

    第二步:把两个节点移动到同一深度

    假设(LCA(x,y))不失一般性,令(deep[x] /leq deep[y])

    先让x往上走(deep[x]-deep[y])步。

    我们将这个差值转化为二进制,然后,通过通过倍增数组往上走(2)的幂次步。
    (即:对于二进制为(1)的第(i)位,往上走(2^i)步)

    或者,还有一种方法:

    从大到小扫描(i),如果(anc[x][i])的深度不小于(y),就跳(x)

    第三步: 求LCA

    在第二步的处理后,假设(x,y)往上走最小的(L)步后是同一节点,也就意味着,(x,y)向上走最大的(L-1)步,是不同的节点。

    我们可以从大到小的枚举(2^i)步,如果当前(x,y)向上走(2^i)步后为同一点,则停止。否则就一起往上走。
    (由于倍增数组的特性,该过程可以看成二分)

    直到不能走为止,再往上走一步就是(LCA)。(PS:(2^0=1)

    时间复杂度:(O((n+q)log_2(n)))

    优点:在线查询,支持动态树。

    代码如下:

    #include <bits/stdc++.h>
    #define MAXN 10000005
    #define MAXLOG 32
    using namespace std;
    int n,m,s;
    int deep[MAXN],anc[MAXN][MAXLOG];
    int cnt=0,adj[MAXN];
    struct EDGE
    {
    	int to,nxt;
    }	e[MAXN];
    void addedge(int u,int v)
    {
    	e[++cnt].to=v; e[cnt].nxt=adj[u]; adj[u]=cnt;
    }
    void dfs(int u,int father)
    {
    	anc[u][0]=father;	deep[u]=deep[father]+1;
    	for(int i=1;i<MAXLOG;++i) anc[u][i]=anc[ anc[u][i-1] ][i-1]; 
    	for(int i=adj[u];i;i=e[i].nxt)
    	{
    		int v=e[i].to;
    		if(v!=father) dfs(v,u);
    	}
    	return ;
    }
    int LCA(int u,int v)
    {
        if(deep[u]<deep[v])
            swap(u,v);
        int h=deep[u]-deep[v];
        for(int i=0;i<MAXLOG;++i) 
        {
            if(h&(1<<i))
            {
                u=anc[u][i];
            }
        }
        if(u==v)
            return u;
        for(int i=MAXLOG-1;i>=0;--i) // -1!!!
        {
            if(anc[u][i]!=anc[v][i])
            {
                u=anc[u][i];
                v=anc[v][i];
            }
        }
        return anc[u][0];
    }
    int main()
    {
    	scanf("%d%d%d",&n,&m,&s);
    	for(int i=1;i<=n-1;++i)
    	{
    		int u,v;scanf("%d%d",&u,&v);
    		addedge(u,v); addedge(v,u);
    	}
    	dfs(s,s);
    	for(int i=1;i<=m;++i)
    	{
    		int a,b; scanf("%d%d",&a,&b);
    		printf("%d
    ",LCA(a,b));
    	}
    	return 0;
    }
    
  • 相关阅读:
    perl 模拟curl 发送json数据
    perl put 发送数据
    8小时浓度均值即连续8个小时浓度的平均值
    awk 字段匹配
    rsyslog imfile配置
    EasyUI datetimebox 的onchange事件的问题
    5大领先的商业智能解决方案,国产上榜!
    5大领先的商业智能解决方案,国产上榜!
    perl post 带中文名字的文件
    Openstack 实现技术分解 (2) 虚拟机初始化工具 — Cloud-Init & metadata & userdata
  • 原文地址:https://www.cnblogs.com/cyl-oi-miracle/p/13871561.html
Copyright © 2011-2022 走看看