您的位置:首页 > 其它

最近公共祖先学习小结1——在线倍增

2016-07-10 11:13 316 查看
学习时看网上资料空讲,难以理解,还是自己动手画一画,才好理解。这里记录下来,以防自己过久了忘记。

切入正题

先了解个概念:

最近公共祖先(Lowest Common Ancestors)(简称LCA),简单讲就是指在树上几个节点共有的祖先节点中,最近的那一个。

例如:3祖先(父亲)节点是2,1、4的祖先(父亲)节点是6,2,1,那么2,1就是这两个点的公共祖先节点,而2离这两个最近,所以2是他们的LCA。

那么怎么求这东西呢?

最简单的就是沿着这个点的父亲往上搜,记录下所有的祖先,然后同样做另外一个点,直到找到第一个相同的祖先。

不难发现这种方法其实很慢,而且可以进行优化,优化的方法很多。

这个优化对后来的倍增有帮助:就是我们不用只搜一个点,而是两个点同时搜。

先将深度更深的那个往上移动,直到两个点为同一层后,再两个点同时往上跳,这样可以快很多。

倍增:这是求LCA的快捷方法之一,而且是在线算法,时间复杂度为nlogn(预处理)+logn(查询一次),是挺快的,只是略慢于离线Tarjan算法,但在线是其一大方面优势。

从上面的优化中,我们可以发现,每次向上跳一个太慢了,我们可以尝试让其跳多个。

设up[x][i]表示x节点的第2^i个祖先是谁,自然有up[x][0]=dad[x];

学过RMQ的知道,up[x][i]=up[up[x][i-1]][i-1],也就是x节点的第2^i个祖先就是x节点第2*(i-1)个祖先的第2^(i-1)个祖先(这话有些绕口,自己画画图)。

至于这个i最大是多少,我们取log2n向上取整,需要提前算出来。

首先我们就先预处理这个up数组:

void dfs(int x)
{
up[x][0]=dad[x];
fo(i,1,M) up[x][i]=up[up[x][i-1]][i-1];
fo(i,1,f[x][0])
if (f[x][i]!=dad[x]) {
int u=f[x][i];
dad[u]=x;
deep[u]=deep[x]+1;
dfs(u);
}
}


接下来就是求祖先了,和上面一样,只是不再一个个跳,而是采用倍增思想,一次跳几个,但注意的是不可以跳过公共祖先哦!

if (deep[x]<deep[y]) swap(x,y);//其实换不换都无所谓了。
down(i,M,0)
if (deep[y]<=deep[up[x][i]]) x=up[x][i];//先把深度深的弄到同一层用倍增思想。
if (x==y) return x;//如果y是x祖先,那么lca就是y了。
down(i,M,0)
if (up[x][i]!=up[y][i]){//注意条件,不能跳过公共祖先。
x=up[x][i];
y=up[y][i];
}
return up[x][0];//不能跳过公共祖先,只能跳到其的子节点,那么这个节点的父亲就是lca


当然,一个LCA不够,还要去掌握离线tarjan。

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  最近公共祖先