LCA(最近公共祖先)tarjan算法学习笔记

给一棵树,问两个节点的最近公共祖先是什么。

    首先要说明的是,tarjan算法是离线的,它一次性读入所有的询问,而且不一定按照读入顺序来处理,但这也是算法的精妙之处;这个算法基于dfs和并查集。

      每当处理好一颗子树的时候,我们就把他们归并到一个集合里,显然如果询问的两个点是在一颗子树里,那么他们的LCA就是这个子树的根,如果不是的话,就可能是这颗子树的根的根(的根...的根

    存储询问也很特别,例如询问a,b,算法把a,b  b,a都存下来,保证完整性

    接下来我就具体的一棵树来讲解:

      LCA(最近公共祖先)tarjan算法学习笔记_第1张图片


    从1开始搜索,我们假设路径为1-2-5-9,接着先判断,在9这个子树(其实是一个叶子)有没有要问的,显然不会有,因为除了这里处理完了,其他地方都没处理,接着回溯上来以后,合并5,9,递归到10,假如这时候询问9,10 或者 10,9 那么答案就已经计算出来了,就是5,也就是(5,9,10)这颗子树的根,如果没有询问,那么以后涉及到这颗子树的某个点的话,它们的LCA必定不是5,一定是5的祖先(的祖先....的祖先),所以当从5回溯到2的时候,我们就要把5和2合并起来,然后把5的祖先修改成2然后再去递归6,其他过程也就类似了。


附:POJ1330代码:点击打开链接

              

                   
#include<stdio.h>                                                          
#include<string.h>                                                                            
#include<iostream>                                                                            
#include<algorithm>                                                                           
#include<vector>                                                                              
                                                                                              
using namespace std;                                                                          
                                                                                              
const int maxn=10005;                                                                         
vector<int>tree[maxn],que[maxn];//树和询问                                                    
bool vis[maxn];                                                                               
int father[maxn];                                                                             
int ancestor[maxn];                                                                           
int rank[maxn];                                                                               
int in[maxn];//入度,找根                                                                     
int n;                                                                                        
                                                                                              
void init()                                                                                   
{                                                                                             
    for (int i=1;i<=n;i++)                                                                    
    {                                                                                         
        rank[i]=1;                                                                            
        father[i]=i;                                                                          
        in[i]=0;                                                                              
        vis[i]=0;                                                                             
        ancestor[i]=0;                                                                        
        que[i].clear();                                                                       
        tree[i].clear();                                                                      
    }                                                                                         
}                                                                                             
                                                                                              
int find(int x)                                                                               
{                                                                                             
    if (x==father[x])                                                                         
        return x;                                                                             
    father[x]=find(father[x]);                                                                
    return father[x];                                                                         
}                                                                                             
                                                                                              
void Union(int x,int y)                                                                       
{                                                                                             
    int a=find(x);                                                                            
    int b=find(y);                                                                            
    if (a!=b)                                                                                 
    {                                                                                         
        if (rank[a]<=rank[b]) //合并的时候按深度浅的向深的合并                                                                
        {                                                                                     
            father[a]=b;                                                                      
            rank[b]+=rank[a];                                                                 
        }                                                                                     
        else                                                                                  
        {                                                                                     
            father[b]=a;                                                                      
            rank[a]+=rank[b];                                                                 
        }                                                                                     
    }                                                                                         
}                                                                                             
                                                                                              
void LCA(int root)                                                                            
{                                                                                             
    ancestor[root]=root;                                                                      
    int size=tree[root].size();                                                               
    for (int i=0;i<size;i++)                                                                  
    {                                                                                         
        LCA(tree[root][i]);                                                                   
        Union(root,tree[root][i]);                                                            
        //printf("%d %d %d %d\n",root,tree[root][i],find(root),ancestor[find(root)]);           
        ancestor[find(root)]=root;                                                 
    }                                                                                         
    vis[root]=1;                                                                              
    size=que[root].size();                                                                    
    for (int i=0;i<size;i++)                                                                  
    {                                                                                         
        if (vis[que[root][i]])                                                                
        {                                                                                     
            printf("%d\n",ancestor[find(que[root][i])]);                                                                     
        }                                                                                     
                                                                                              
    }                                                                                         
}                                                                                             
                                                                                              
int main()                                                                                    
{                                                                                             
    int t;                                                                                    
    scanf("%d",&t);                                                                           
    while (t--)                                                                               
    {                                                                                         
        int x,y;                                                                              
        scanf("%d",&n);                                                                       
        init();                                                                               
        for (int i=0;i<n-1;i++)                                                               
        {                                                                                     
            scanf("%d%d",&x,&y);                                                              
            tree[x].push_back(y);                                                             
            in[y]++;                                                                          
        }                                                                                     
        scanf("%d%d",&x,&y);                                                                  
        que[x].push_back(y);                                                                  
        que[y].push_back(x);                                                                  
        for (int i=1;i<=n;i++)                                                                
        {                                                                                     
            if (in[i]==0)                                                                     
            {                                                                                 
                LCA(i);                                                                       
                break;                                                                        
            }                                                                                 
        }                                                                                     
    }                                                                                         
    return 0;                                                                                 
}                                                                                             


你可能感兴趣的:(LCA)