zoukankan      html  css  js  c++  java
  • tarjan算法求最近公共祖先

    tarjian算法

    LCA: LCA(Least Common Ancestor),顾名思义,是指在一棵树中,距离两个点最近的两者的公共节点。也就是说,在两个点通往根的道路上,肯定会有公共的节点,我们就是要求找到公共的节点中,深度尽量深的点。还可以表示成另一种说法,就是如果把树看成是一个图,这找到这两个点中的最短距离。

    LCA算法有在线算法也有离线算法,所谓的在线算法就是实时性的,而离线算法则是要求一次性读入所有的请求,然后在统一得处理。而在处理的过程中不一定是按照请求的输入顺序来处理的。说不定后输入的请求在算法的执行过程中是被先处理的。

    tarjan算法。这个算法是基于并查集和DFS的。离线算法。

    现在我们来观察正在处理与x结点关联的询问时并查集的情况。由于一个结点处理完毕后,它就被归到其父结点所在的集合,所以在已经处理过的结点中(包括 x本身),x结点本身构成了与x的LCA是x的集合,x结点的父结点及以x的所有已处理的兄弟结点为根的子树构成了与x的LCA是father[x]的集合,x结点的父结点的父结点及以x的父结点的所有已处理的兄弟结点为根的子树构成了与x的LCA是father[father[x]]的集合……(上面这几句话如果看着别扭,就分析一下句子成分,也可参照右面的图)假设有一个询问(x,y)(y是已处理的结点),在并查集中查到y所属集合的根是z,那么z 就是x和y的LCA,x到y的路径长度就是 lv[x]+lv[y]-lv[z]*2 。累加所有经过的路径长度就得到答案。  现在还有一个问题:上面提到的询问(x,y)中,y是已处理过的结点。那么,如果y尚未处理怎么办?其实很简单,只要在询问列表中加入两个询问(x, y)、(y,x),那么就可以保证这两个询问有且仅有一个被处理了(暂时无法处理的那个就pass掉)。而形如(x,x)的询问则根本不必存储。  如果在并查集的实现中使用路径压缩等优化措施,一次查询的复杂度将可以认为是常数级的,整个算法也就是线性的了。

    Tarjan作为离线off-line算法,在程序开始前,需要将所有等待询问的节点对提前存储,然后程序从树根开始执行TarjanLCA()。假如有如下一棵多叉树

    根据TarjanLCA的实现算法可以看出,只有当某一棵子树全部遍历处理完成后,才将该子树的根节点标记为黑色(初始化是白色),假设程序按上面的树形结构进行遍历,首先从节点1开始,然后递归处理根为2的子树,当子树2处理完毕后,节点2, 5, 6均为黑色;接着要回溯处理3子树,首先被染黑的是节点7(因为节点7作为叶子不用深搜,直接处理),接着节点7就会查看所有询问(7, x)的节点对,假如存在(7, 5),因为节点5已经被染黑,所以就可以断定(7, 5)的最近公共祖先就是find(5).ancestor,即节点1(因为2子树处理完毕后,子树2和节点1进行了union,find(5)返回了合并后的树的根1,此时树根的ancestor的值就是1)。    有人会问如果没有(7, 5),而是有(5, 7)询问对怎么处理呢?我们可以在程序初始化的时候做个技巧,将询问对(a, b)和(b, a)全部存储,这样就能保证完整性。

    如果要按顺序输出,在输入询问时打一个标记,假设开一个数组 f[][] , f[x][] = "顺序",

    que[x][i]=y 是询问x,y,与x有关的第 i 个数是y,f[x][i] = "顺序" ,与x有关的第 i 个询问是的几个顺序,所以就可以表示输出的顺序了。

     模板

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<algorithm>
     4 #include<vector>
     5 #include<cstring>
     6 using namespace std;
     7 
     8 const int N = 10010;
     9 
    10 vector<int>node[N];    //相连的边 
    11 vector<int>que[N];  //询问 
    12 int n,m,x,y;
    13 int far[N],ance[N];    //祖先,集合 
    14 int rank[N],ru[N];    //树的秩,入度 
    15 bool vis[N];        //判断是否访问过 
    16 
    17 void init()  
    18 {  
    19     for(int i=0;i<=n;i++)  
    20     {  
    21         node[i].clear();  
    22         que[i].clear();  
    23         far[i] = i;
    24         rank[i] = 1;
    25     }
    26 }  
    27 
    28 int find(int x)
    29 {
    30     return far[x]==x?x:far[x]=find(far[x]);    
    31 }
    32 
    33 void unio(int a,int b)
    34 {
    35     a=find(a);  
    36     b=find(b);  
    37     if(a==b) return ;  
    38     if(rank[a]<=rank[b])  
    39     {  
    40         far[a] = b;  
    41         rank[b] += rank[a];  
    42     }  
    43     else   
    44     {  
    45         far[b] = a;
    46         rank[a] += rank[b];  
    47     }  
    48 }
    49 
    50 void LCA(int root)  
    51 {      
    52     ance[root] = root;         //首先自成一个集合  
    53     for(int i=0;i<node[root].size();i++)  
    54     {
    55         LCA(node[root][i]);        //递归子树  
    56         unio(root,node[root][i]);    //将子树节点与根节点root的集合合并 
    57         ance[find(root)] = root;    //合并后的集合的祖先为root 
    58     }
    59     vis[root] = true;
    60     for(int i=0;i<que[root].size();i++)  
    61     {  
    62         if(vis[que[root][i]])    //如果另一节点已经访问过 
    63         printf("%d和%d的最近公共祖先:%d
    ", root,que[root][i],ance[find(que[root][i])]);
    64     }
    65 }  
    66 
    67 int main()
    68 {
    69     scanf("%d%d",&n,&m);
    70     init();      //调试了好久,原来这里的初始化要放在输入n下面 
    71     for(int i=1;i<n;i++)  
    72     {  
    73         scanf("%d%d",&x,&y);   //x->y有一条边
    74         node[x].push_back(y);
    75         //node[y].push_back(x);  
    76         ru[y]++;  
    77     }  
    78     for(int i=1;i<=m;++i)
    79     {
    80         scanf("%d%d",&x,&y);  
    81         que[x].push_back(y); 
    82         que[y].push_back(x); 
    83     }
    84     for(int i=0;i<=n;++i)
    85         if(ru[i]==0) LCA(i);    //寻找根节点 
    86     return 0;
    87 }
  • 相关阅读:
    网易云易盾牵手百视通 助力广电领域新媒体内容安全
    理解DDoS防护本质:基于资源较量和规则过滤的智能化系统
    DDoS防护之TCP防护
    2017年内容安全十大事件盘点
    知物由学 | AI时代,那些黑客正在如何打磨他们的“利器”?(一)
    应对羊毛党的老手段不管用了,但有些公司依然有办法,他们是怎么做的?
    知物由学 | 未来安全隐患:AI的软肋——故意欺骗神经网络
    MYSQL数据库的数据完整性
    MYSQL是什么?
    python多线程实现多任务
  • 原文地址:https://www.cnblogs.com/mjtcn/p/6852646.html
Copyright © 2011-2022 走看看