最近公共祖先(LCA)及其倍增算法实现

来源:互联网 发布:淘宝商品质量问题定义 编辑:程序博客网 时间:2024/05/21 10:41

最近公共祖先(LCA)

今天看看最近公共祖先(LCA),也就是所谓的最小公共祖先。
我们首先了解一下什么是LCA,我们通过几棵树来理解一下吧。
图一
如图所示,这棵树是以1为根节点的一棵树,我们举一个例子,3和5的LCA就是2,4和5的LCA就是1,3和2的LCA就是2本身。是不是有点明白?
接下来,我们不改变节点间的关系,只改变根节点。
图二
如图所示,我们把2作为根节点,那么这棵二叉树俨然就变成了多叉树,然后我们再举几个例子,比如说,3和5的LCA仍然是2,但4和5的LCA就变成了2,所以我们发现,根节点选取的改变,也会导致两点之间LCA的数值的变化。
理清概念之后我们就上一个模板题来看看,这个题的样例可以自己出,在这里不再给大家提供样例,话说可以去luogu过一下嘞。

题目描述
如题,给定一棵有根多叉树,请求出指定两个点直接最近的公共祖先。
输入格式:
第一行包含三个正整数N、M、S,分别表示树的结点个数、询问的个数和树根结点的序号。
(数据保证可以构成树)。
接下来M行每行包含两个正整数a、b,表示询问a结点和b结点的最近公共祖先。
输出格式:
输出包含M行,每行包含一个正整数,依次为每一个询问的结果。 时空限制:1000ms,128M

学过最小生成树(MST)的人都应该知道,像这样的树,它的边数为N-1条。所以就算是题目中不说,我们也认为它是N-1条。
接下来我们就一步一步的解释:

在这里我们先提前引入一个数组,稍后讲倍增的时候会解释。fa[i][j]的含义就是,对于一个节点i,它的第2^j个父亲节点的编号,比如说上面第一个图中fa[5][0]=2,fa[5][1]=1。

做好图论题,比较基础且重要的就是建图,利用邻接表,从根节点出发,用DFS来建表。与其它题不同的一点就是这里每次son的建边都需要对它的fa[son][]数组进行更新,fa[son][i+1]=fa[fa[son][i]][i]。理解这一点很重要,这里就已经有倍增算法的思想了,可以自己任意画一棵树来试一试,是很奇妙的。

然后求LCA。

对于两个点X和Y,一个比较自然的思路就是,既然要找他们的LCA,就一个一个的顶上去就行呗,等到他们走过的路径有节点的时候就输出。这样做确实能得到正确的答案,但是会超时。

我们的思路是,首先保证deep[X]>=deep[Y],这保证了我们在操作的时候只操作X(当然只操作Y也是可以的)。然后让X向上搜,直至其深度等于Y的深度,这里通过倍增算法让其快速上搜,稍后我们解释倍增算法。等到二者深度相同的时候,就让他们同时上溯至同一节点,此节点即为X与Y节点的LCA。

先把自己会的搞上去以便于理解。
建边操作(话说我前面写的几个博客的建边操作太鬼畜了有木有),用3个数组来存边的属性,就是邻接表。

void add(int xx,int yy){    nxt[++temp]=hd[xx];    y[temp]=yy;    hd[xx]=temp;}

然后是建图,上面也已经说了,需要注意的就是同一个节点不能被经历两次,所以用一个bool数组来记忆。

void dfs(int x){    int p,son;    p=hd[x];    while(p)    {        son=y[p];        if(!b[son])        {            b[son]=1;            fa[son][0]=x;            deep[son]=deep[x]+1;            int ii=0,po=x;            while(fa[po][ii]!=0)            {                fa[son][ii+1]=fa[po][ii];                po=fa[po][ii];                ii++;            }            dfs(son);        }        p=nxt[p];    }}

接下来重点说倍增算法在LCA中的应用(详见注释)。

int lca(int xx,int yy){    int i,j;    if(deep[xx]<deep[yy])swap(xx,yy);    for(i=0; (1<<i)<=deep[xx]; i++);    i--;//找当前点到树根距离大小的2^j距离中,j的最大值。比如说:deep[xx]=3时,jmax=2,deep[xx]=9时,jmax=3。    for(j=i; j>=0; j--)        if(deep[xx]-(1<<j)>=deep[yy])            xx=fa[xx][j];    if(xx==yy)return xx;//当xx与yy同深度时,恰好就在yy这个位置上,那么就直接xx或者yy是LCA    for(j=i; j>=0; j--)    {        if(fa[xx][j]!=fa[yy][j])//这里的IF语句功能比较多,一方面可以筛掉超过当前点到树根距离大小的2^j距离,还可以在二者相同时不操作它,因为已经达成找到LCA的小目标了。        {            xx=fa[xx][j];            yy=fa[yy][j];        }    }    return fa[xx][0];}

这里LCA讲的可能比较糙,思路可以看看这个博客:
http://www.cnblogs.com/yyf0309/p/5972701.html
我是从这里学会的LCA的倍增实现的:
http://blog.csdn.net/wangwangbu/article/details/51453084
最后,有任何疑问欢迎评论区提出!
这里写图片描述

原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 路由器信号太强怎么办 wifi被破解了怎么办 眼睛被电光刺伤怎么办 被紫外线灯照射怎么办 浴巾洗了发硬怎么办 枕巾上的头油怎么办 洗手盆缝隙漏水怎么办 洗手盆裂缝漏水怎么办 洗手盆堵了怎么办 征信账号注册怎么办 注册不了征信号怎么办 阿里巴巴一键铺货到淘宝发货怎么办 淘宝购物的问题怎么办 买家评价被删除怎么办 淘宝订单虚假交易怎么办 被判定虚假物流怎么办 淘宝有虚假交易怎么办 微信辅助不了怎么办 微信验证失败怎么办 淘宝占空间太大怎么办 淘宝占用空间大怎么办 ipad空间不够用怎么办 ipadmini密码忘了怎么办 旧ipad特别卡怎么办 苹果ipad反应慢怎么办 手机垃圾多了怎么办 ipad2内存过低怎么办 苹果平板ipad内存不足怎么办 手机dns配置错误怎么办 蓝牙已停止运行怎么办 ipad看电视闪退怎么办 ipad为什么看电视会闪退怎么办 微淘直播延迟怎么办 手机淘宝进群领金币怎么办 做淘客冲销量停止淘客后怎么办 微信中零钱提现怎么办 淘宝买家不签收怎么办 小龙虾没人下单怎么办 淘宝直播不浮现怎么办 淘宝直播看不了怎么办 理财客户说没钱怎么办