最近公共祖先(Tarjan算法)
来源:互联网 发布:猎鲸狂人软件 编辑:程序博客网 时间:2024/06/06 08:32
最近公共祖先(Tarjan离线算法)
当面对一个图中要求任意两点间的最短路,用kruskal或者prim算法显然会很耗时间。利用最近公共祖先我们就非常的方便快捷。
算法复杂度:
LCA的离线算法。复杂度为O(n+q)。
算法分析:
这个算法充分利用了dfs树的结构。
对于每个节点u,关于它的询问(u,v)只有两种。(假设先dfs(u)后dfs(v))
1、v在u的子树内
此时LCA(u,v) = u.
2、v不在u的子树内
⑴假设v在u的父亲的另一棵子树内。
此时LCA(u,v) = father[u].
⑵如果不满足条件⑴,则v可能在u的父亲的父亲的另一棵子树内。
而此时LCA(u,v) = father[ father[u] ].
⑶……
观察一下,是不是发现了什么呢?
没错,不论是哪种情况,LCA(u,v)都与u和father[ ]有某种关系。我们能不能抓住这种关系呢?
我们继续观察,一直向上取father[ ],貌似和并查集的FIND操作很像呢。
我们用并查集的角度依次考虑上面的情况试试看。
1、v在u的子树内
此时dfs(u)还在栈中,没有执行完,此时没有向上取father[ ],说明此时u是根。
2、v不在u的子树内
⑴假设v在u的父亲的另一棵子树内。
此时的dfs(u)已经执行完并出栈。此时向上取了一次father[ ],说明此时u的父亲是根。
⑵如果不满足条件⑴,则v可能在u的父亲的父亲的另一棵子树内。
同理,此时dfs(u的父亲)也已经执行完并出栈。此时向上取了两次father[ ],说明此时u的父亲的父亲是根。
⑶……
综上,我们只要保证当dfs(u)在栈中的时候,u是根;当dfs(u)不在栈中的时候,father[u]是根就行了。
#include <stdio.h>#include <string.h>#include <algorithm>using namespace std;#define FileIn freopen("in.ads","r",stdin)#define FileOut freopen("out.ads","w",stdout)#define N 155#define M 22555struct Vertex{ int head;}V[N],Qv[N];struct Edge{ int v,next;}E[M],Qe[M];int top,pre[N];bool used[N];void Init(){ top = 0; memset(V,-1,sizeof(V)); memset(Qv,-1,sizeof(Qv)); memset(pre,-1,sizeof(pre)); memset(used,0,sizeof(used));}int Root(int x){ if(pre[x] != -1) return pre[x] = Root(pre[x]); else return x;}void Union(int a,int b){ int r1 = Root(a); int r2 = Root(b); if(r1 != r2) pre[r2] = r1;}void Add_Edge(int u,int v){ E[top].v = v; E[top].next = V[u].head; V[u].head = top++; /*printf("E[%d].v = %d\n", top-1, v); printf("E[%d].next = V[%d].head\n",top-1, u); printf("V[%d].head = %d++\n", u, top-1);*/}void Add_Qedge(int u,int v){ Qe[top].v = v; Qe[top].next = Qv[u].head; Qv[u].head = top++;}void Tarjan(int u){ used[u] = true; for(int i=Qv[u].head;i!=-1;i=Qe[i].next) { int v = Qe[i].v; if(used[v]) printf("The LCA of (%d,%d) is -> %d\n",u,v,Root(v)); } for(int i=V[u].head;i!=-1;i=E[i].next) { int v= E[i].v; if(used[v]) continue; Tarjan(v); Union(u,v); }}int main(){ //FileIn; int n,m,u,v,Q; while(~scanf("%d%d",&n,&m)) { Init(); while(m--) { scanf("%d%d",&u,&v); Add_Edge(u,v); Add_Edge(v,u); } scanf("%d",&Q); while(Q--) { scanf("%d%d",&u,&v); Add_Qedge(u,v); Add_Qedge(v,u); } Tarjan(1); }return 0;}
0 0
- 最近公共祖先(Tarjan算法)
- [算法] LCA 最近公共祖先 (Tarjan)
- Tarjan算法求LCA(最近公共祖先)
- Tarjan离线算法求最近公共祖先(LCA)
- Tarjan离线算法求最近公共祖先(LCA)
- LCA(最近公共祖先)tarjan算法学习笔记
- 最近公共祖先LCA:Tarjan算法(介绍1)
- 最近公共祖先LCA:Tarjan算法(介绍2)
- Tarjan离线算法求最近公共祖先(LCA)
- [图论] LCA(最近公共祖先)Tarjan 离线算法
- Tarjan算法求LCA(最近公共祖先)
- LCA(最近公共祖先)离线算法Tarjan
- [图论] LCA(最近公共祖先)Tarjan 离线算法
- POJ 1986 Tarjan离线算法(最近公共祖先)
- c++最近公共祖先LCA(倍增算法和tarjan)
- HDU2586(最近公共祖先的Tarjan算法)
- LCA最近公共祖先(tarjan离线算法)
- 最近公共祖先LCA:Tarjan算法
- 《小团队项目管理》第一问 --- 需求调研需要舌辨群雄?
- CentOS自动登陆
- android SharedPreferences 数据存储
- jQuery+ashx实现加载更多(C#)
- poj 3278
- 最近公共祖先(Tarjan算法)
- TCP/UDP通信模型
- 如何修改计算机名,才能用该用户名远程
- C语言基础知识之#pragma once
- 提示框
- DWR A request has been denied as a potential CSRF atta 的两种情况
- UIViewController 视图控制器
- leetcode之Triangle
- 字符串包含一个或多个数字,编写函数把数字字符转化为整数并返回这个整数。如果字符串包含任何非数字字符,函数就返回零。