最近公共祖先LCA(Tarjan与DFS--ST倍增)
来源:互联网 发布:严宽乔振宇 知乎 编辑:程序博客网 时间:2024/06/04 18:34
定义
LCA(Least Common Ancestors),即最近公共祖先,是指在有根树中,找出某两个结点u和v最近的公共祖先。
解决方案
最简单的LCA问题即给你一棵
朴素算法
一般能想到的最暴力的方法就是:对于两个节点x,y,不断把深度较大的一个往上移,直到两个点相遇。
但是显然这样模拟是非常慢的,复杂度可以达到
于是我们可以采取以下两种方法。
DFS–ST算法(在线)
算法思想
既然一个一个移这么慢,有没有什么办法能移得快一点呢?答案当然是肯定的。
用ST表啊(没学过不要紧,我也是先学LCA再学ST的)!倍增多快对不对!
算法实现
类似的,定义
而对于其它的
对于
fa[i][j]=fa[fa[i][j-1]][j-1];
然后就可以一下子跳一大步啦!
时间复杂度
代码实现:
void dfs(int x,int dep){//先DFS记录深度与fa[i][0] depth[x]=dep; for (int i=h[x];i;i=ed[i].next)//邻接表 if (ed[i].to!=fa[x][0]){ fa[ed[i].to][0]=x; dfs(ed[i].to,dep+1); }}void make(){//递推 for (int j=1;j<=19;j++)//j要放在最外层 for (int i=1;i<=n;i++) fa[i][j]=fa[fa[i][j-1]][j-1];//递推式}int LCA(int x,int y){//寻找LCA if (depth[x]<depth[y]) swap(x,y); for (int i=19;i>=0;i--)//一定要先跳大步,不然会跳漏掉,下同 if (depth[fa[x][i]]>=depth[y]) x=fa[x][i];//先把x与y调到同一深度 if (x==y) return x;//如果恰好相等,直接返回 for (int i=19;i>=0;i--)//两个一起跳,跳到两者的父亲一样为止 if (fa[x][i]!=fa[y][i]){ x=fa[x][i]; y=fa[y][i]; } return fa[x][0];}
Tarjan算法(离线)
算法思想:
有些时候题目允许你离线操作,这时便可以采用另一种方法:Tarjan。
Tarjan其实就是一个DFS的过程,对于节点
根据DFS的性质,如果
可能解释的不是很清楚,读者们可以画画图体会一下我太懒啦
时间复杂度
算法实现
具体实现只需要按照这几个步骤即可:
①:从根节点开始遍历(DFS)。
②:遍历节点x的子节点,并标记其为已访问。
③:把子节点合并到x上。
④:遍历x的询问,如果发现y已标记,则该询问的答案即为findfather(y)。
代码:
int findfather(int x){//并查集查询+路压 if (fa[x]==x) return x; return fa[x]=findfather(fa[x]);}void Tarjan(int x){ f[x]=true; fa[x]=x;//标记 for (int i=h[x];i;i=ed[i].next)//遍历子节点 if (!f[ed[i].to]){ Tarjan(ed[i].to); fa[ed[i].to]=x; } for (int i=h1[x];i;i=ed[i].next)//遍历询问 if (f[ed[i].to]) ans[ed[i].num]=findfather(ed[i].to);}
模板
以洛谷P3379为例:
先放两张图感受下ST与Tarjan的快慢
ST:
Tarjan:
差距自行体会(两者均加了读优)
ST代码:
#include<cstdio>#include<cstring>#include<algorithm>#define MAXN 500000using namespace std;struct edge{ int next,to;};int n,s,q,k;int fa[MAXN+5][20],h[MAXN+5],depth[MAXN+5];edge ed[MAXN*2+5];inline char readc(){//读优 static char buf[100000],*l=buf,*r=buf; if (l==r) r=(l=buf)+fread(buf,1,100000,stdin); if (l==r) return EOF; return *l++;}inline int _read(){ int num=0; char ch=readc(); while (ch<'0'||ch>'9') ch=readc(); while (ch>='0'&&ch<='9') { num=num*10+ch-48; ch=readc(); } return num;}void addedge(int x,int y){ ed[++k].next=h[x]; ed[k].to=y; h[x]=k;}void dfs(int x,int dep){ depth[x]=dep; for (int i=h[x];i;i=ed[i].next) if (ed[i].to!=fa[x][0]){ fa[ed[i].to][0]=x; dfs(ed[i].to,dep+1); }}void make(){ for (int j=1;j<=19;j++) for (int i=1;i<=n;i++) fa[i][j]=fa[fa[i][j-1]][j-1];}int LCA(int x,int y){ if (depth[x]<depth[y]) swap(x,y); for (int i=19;i>=0;i--) if (depth[fa[x][i]]>=depth[y]) x=fa[x][i]; if (x==y) return x; for (int i=19;i>=0;i--) if (fa[x][i]!=fa[y][i]){ x=fa[x][i]; y=fa[y][i]; } return fa[x][0];}int main(){ n=_read(); q=_read(); s=_read(); for (int i=1;i<n;i++){ int u=_read(),v=_read(); addedge(u,v); addedge(v,u); } fa[s][0]=s; dfs(s,0); make(); while (q--){ int u=_read(),v=_read(); printf("%d\n",LCA(u,v)); } return 0;}
Tarjan代码:
#include<cstdio>#include<cstring>#include<algorithm>#include<vector>#define MAXN 500000using namespace std;struct edge{ int next,to,num;};int n,s,q,k;int fa[MAXN+5],h[MAXN+5],ans[MAXN+5],h1[MAXN+5];edge ed[MAXN*4+5];bool f[MAXN+5];inline char readc(){ static char buf[100000],*l=buf,*r=buf; if (l==r) r=(l=buf)+fread(buf,1,100000,stdin); if (l==r) return EOF; return *l++;}inline int _read(){ int num=0; char ch=readc(); while (ch<'0'||ch>'9') ch=readc(); while (ch>='0'&&ch<='9') { num=num*10+ch-48; ch=readc(); } return num;}void addedge(int x,int y,int *h,int num){ ed[++k].next=h[x]; ed[k].to=y; ed[k].num=num; h[x]=k;}int findfather(int x){ if (fa[x]==x) return x; return fa[x]=findfather(fa[x]);}void Tarjan(int x){ f[x]=true; fa[x]=x; for (int i=h[x];i;i=ed[i].next) if (!f[ed[i].to]){ Tarjan(ed[i].to); fa[ed[i].to]=x; } for (int i=h1[x];i;i=ed[i].next) if (f[ed[i].to]) ans[ed[i].num]=findfather(ed[i].to);}int main(){ n=_read(); q=_read(); s=_read(); for (int i=1;i<n;i++){ int u=_read(),v=_read(); addedge(u,v,h,0); addedge(v,u,h,0); } for (int i=1;i<=q;i++){ int u=_read(),v=_read(); addedge(u,v,h1,i); addedge(v,u,h1,i); } Tarjan(s); for (int i=1;i<=q;i++) printf("%d\n",ans[i]); return 0;}
- 最近公共祖先LCA(Tarjan与DFS--ST倍增)
- 最近公共祖先(LCA):tarjan与倍增
- 最近公共祖先 LCA 倍增+Tarjan实现
- lca最近公共祖先(st表/倍增)
- c++最近公共祖先LCA(倍增算法和tarjan)
- luogu3379 最近公共祖先(LCA) tarjan 倍增
- 最近公共祖先(LCA)---倍增法
- 最近公共祖先(LCA):倍增
- LCA最近公共祖先(RMQ、Tarjan)
- [算法] LCA 最近公共祖先 (Tarjan)
- 最近公共祖先(LCA)算法实现过程 【Tarjan离线+倍增在线+RMQ】
- 最近公共祖先LCA tarjan
- LCA最近公共祖先的离线算法(Tarjan)和在线算法(ST)
- LCA(最近公共祖先)的多种实现方法——dfs、倍增
- 【讲解+模板】最近公共祖先(LCA)(倍增)
- LCA最近公共祖先(朴素+倍增法)
- 树上倍增求LCA(最近公共祖先)
- lca(最近公共祖先)倍增模板【pascal】
- 空姐秋冬护唇秘籍,顶着沙漠嘴谁会想亲你 | 钛空舱
- 信手拈来按键帮你来 4.8.0001 最新版发布
- 群雄混战的短视频,繁荣背后存在的短板
- hdu 1166 敌兵布阵
- Activiti工作流开发——Activiti相关概念
- 最近公共祖先LCA(Tarjan与DFS--ST倍增)
- 《Node.js在CLI下的工程化体系实践》成都OSC源创会分享总结
- 喜迎十九大的召开,无穷小微积分即将浮出水面
- Nginx +tomcat 实现负载均衡集群
- 多态继承中的内存图解
- 虽然微不足道但是很有用的JAVA基础—函数
- 多态中的对象变化内存图解
- Product of Array Except Self_Week5
- Unity3D脚本中创建的gameobject如何删除