LCA(最近公共祖先)——离线 Tarjan 算法

Posted GGBeng

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了LCA(最近公共祖先)——离线 Tarjan 算法相关的知识,希望对你有一定的参考价值。

tarjan算法的步骤是(当dfs到节点u时):
1 在并查集中建立仅有u的集合,设置该集合的祖先为u
1 对u的每个孩子v:
   1.1 tarjan之
   1.2 合并v到父节点u的集合,确保集合的祖先是u
2 设置u为已遍历
3 处理关于u的查询,若查询(u,v)中的v已遍历过,则LCA(u,v)=v所在的集合的祖先

不懂请点击

 伪代码:

Tarjan(u)//marge和find为并查集合并函数和查找函数
{
    for each(u,v)    //访问所有u子节点v
    {
        Tarjan(v);        //继续往下遍历
        marge(u,v);    //合并v到u上
        标记v被访问过;
    }
    for each(u,e)    //访问所有和u有询问关系的e
    {
        如果e被访问过;
        u,e的最近公共祖先为find(e);
    }
}

模板:

  1 #include<iostream>
  2 #include<cstdio>
  3 #include<vector>
  4 #include<algorithm>
  5 using namespace std;
  6 const int maxn = 10010;
  7 
  8 int rank[maxn];            //并查集结点高度 
  9 int pre[maxn];            //并查集父节点 
 10 int anc[maxn];            //祖先 
 11 int indgree[maxn];        //保存每个节点的入度 
 12 bool vis[maxn]; 
 13 vector<int> tree[maxn];    //保存树
 14 vector<int> qes[maxn];    //保存查询 
 15 int vertex_num;            //顶点数 
 16 
 17 void init()
 18 {
 19     memset(rank,1,sizeof(rank));
 20     memset(vis,0,sizeof(vis));
 21     memset(indgree,0,sizeof(indgree));
 22     memset(anc,0,sizeof(anc));
 23     for(int i=1;i<=vertex_num;i++){
 24         pre[i] = i;
 25         tree[i].clear();
 26         qes[i].clear();
 27     }
 28 }
 29 
 30 int find_pre(int x)        //查找函数 
 31 {
 32     if(pre[x] == x){
 33         return x;
 34     }
 35     else{
 36         pre[x] = find_pre(pre[x]);        //压缩路径 
 37     }
 38     return pre[x];
 39 }
 40 
 41 void Union(int x,int y)
 42 {
 43     x = find_pre(x);
 44     y = find_pre(y);
 45     if(x == y){
 46         return ;
 47     }
 48     else{
 49         if(rank[x] > rank[y]){
 50             pre[y] = x;
 51         }
 52         else if(rank[x] == rank[y]){        //x和y深度相同时, y向x合并 
 53             rank[x]++;
 54             pre[y] = x;
 55         }
 56         else{
 57             pre[x] = y;
 58         }
 59     }
 60 }
 61 
 62 int lca(int u)
 63 {
 64     anc[u] = u;
 65     for(int i=0;i<tree[u].size();i++){
 66         lca(tree[u][i]);
 67         Union(u,tree[u][i]);
 68         anc[find_pre(u)]=u;
 69     } 
 70     vis[u] = 1;
 71     for(int i=0;i<qes[u].size();i++){
 72         if(vis[qes[u][i]] == 1){
 73             return anc[find_pre(qes[u][i])];
 74         }
 75     }
 76 }
 77 
 78 int main()
 79 {
 80     cout<<"请输入顶点数:"; 
 81     cin>>vertex_num;
 82     init();
 83     int u,v,ans;
 84     cout<<"请依次输入各边:\\n";
 85     for(int i=1;i<vertex_num;i++){
 86         cin>>u>>v;
 87         tree[u].push_back(v);
 88         indgree[v]++; 
 89     }
 90     cout<<"请输入要查询的两个数:";
 91     cin>>u>>v;
 92     qes[u].push_back(v);
 93     qes[v].push_back(u);
 94     for(int i=1;i<=vertex_num;i++){
 95         if(indgree[i] == 0){
 96             ans = lca(i);
 97             printf("(%d,%d)的LCA是:%d\\n",u,v,ans);
 98             break;
 99         }
100     }
101     return 0;
102 }

上一个图,来测试模板:

 

题目:POJ 1470

代码:点击

/* 
 * POJ 1470 
 * 给出一颗有向树,Q个查询 
 * 输出查询结果中每个点出现次数 
 */ 
/* 
 * LCA离线算法,Tarjan 
 * 复杂度O(n+Q); 
 */ 

 

以上是关于LCA(最近公共祖先)——离线 Tarjan 算法的主要内容,如果未能解决你的问题,请参考以下文章

LCA(最近公共祖先)--tarjan离线算法 hdu 2586

LCA最近公共祖先 Tarjan离线算法

tarjan算法求最近公共祖先

算法树上公共祖先的Tarjan算法

最近公共祖先LCA(Tarjan算法)的思考和算法实现——转载自Vendetta Blogs

Tarjan 算法求 LCA / Tarjan 算法求强连通分量