Tarjan的脱机最小公共祖先算法

论坛 期权论坛 编程之家     
选择匿名的用户   2021-6-2 20:58   1749   0

在一棵有根树T中,两个节点u和v的最小公共祖先是指这样的一个节点w,它是u和v的祖先,并且在树T中具有最大深度。

Tarjan思想求LCA是从根节点x开始搜索每一棵子树(节点设为y),那么在回溯回子树根节点y的时候就能保证以该子树的全部节点搜索完了,在每搜索完一个子树,那么该子树内的所有LCA(u,v)的问题都已经解决了,其他的LCA询问肯定在另外的子树上,那么这时就把y放入到合并的集合中,这样y结点和所有y的子树中的结点的最近公共祖先就是y了,节点y和当前还没有遍历的y的兄弟节点和这些兄弟节点的子节点的公共祖先都是y的父亲节点。

LCA伪代码:

LCA(u){
    MAKE_SET(u)
    ancestor[FIND(u)]= u//设定u所在集合的公共祖先
    for( each child v of u in T){
        LCA(v)
        UNION(u,v)//把节点v生成的子集并入u中
        ancestor[FIND(u)]=u//防止采用树形启发式合并使u的集合根(代表)变化
    }
    flag[u]= 1;
    for( each node v such that [u,v] in P )
     if( flag[v] ) 
      print "The least common ancestor of 'u' and 'v' is " ancestor[ FIND(v) ]
}


POJ 1330

int anc[N],fa[N],in[N],flag[N],rank[N];
int root;
vector<int>mat[N],qes[N];

void init(int n)
{
    memset(anc,0,sizeof(anc));
    memset(flag,0,sizeof(flag));
    memset(rank,0,sizeof(rank));
    memset(in,0,sizeof(in));
    for(int i = 0 ; i <= n ; i ++) {
        fa[i] = i;
        mat[i].clear();
        qes[i].clear();
    }
    for(int i = 1 ; i < n ; i ++)
    {
        int x,y;
        scanf("%d %d",&x,&y);
        mat[x].push_back(y);
        in[y]++;
    }
}

int find(int x)
{
    if(x != fa[x]) fa[x] = find(fa[x]);
    return fa[x];
}

void link(int x,int y)
{
    int a = find(x),b = find(y);
    if(rank[a] > rank[b]) fa[b] = a;
    else fa[a] = b;
    if(rank[a] == rank[b]) rank[b]++;
}


void LCA(int root)
{

    anc[find(root)] = root;
    for(int i = 0 ; i < mat[root].size() ; i ++)
    {
        LCA(mat[root][i]);//对以节点mat[root][i]为根节点的子树进行搜索
        link(root,mat[root][i]);//用并查集将两节点合并
        anc[find(root)] = root;

    }
    flag[root] = 1;
    for(int i = 0 ; i < qes[root].size() ; i ++)
    {
        if(flag[qes[root][i]])
        {
            printf("%d\n",anc[find(qes[root][i])]);
            return;
        }
    }
}

int main()
{
    int T,n;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&n);
        init(n);
        int u,v;
        scanf("%d %d",&u,&v);
        qes[u].push_back(v);
        qes[v].push_back(u);
        for(int i = 1; i <= n ; i ++)
        {
            if(in[i] == 0)
            {
                root = i;
                break;
            }
        }
        LCA(root);
    }
    return 0;
}


lca另一种写法

void LCA(int root)
{
fa[root] = root;
for(int i = 0 ; i < mat[root].size() ; i ++)
{
if(!flag[mat[root][i]]){
LCA(mat[root][i]);//对以节点mat[root][i]为根节点的子树进行搜索
fa[mat[root][i]] = root;
}
}
flag[root] = 1;
for(int i = 0 ; i < qes[root].size() ; i ++)
{
if(flag[qes[root][i]])
{
printf("%d\n",find(qes[root][i]));
return;
}
}
}


分享到 :
0 人收藏
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

积分:3875789
帖子:775174
精华:0
期权论坛 期权论坛
发布
内容

下载期权论坛手机APP