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

在一棵有根树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
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值