算法一:朴素法
采用两点逐渐向根移动的方法,求出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;
}