LCA(least common ancestors)最近公共祖先

http://blog.csdn.net/hnust_xiehonghao/article/details/9109295

对于有根树T的两个节点u、v,最近公共祖先LCA(u,v)表示一个节点x,满足x是u,v的祖先,且x的深度尽可能大
如果把树看成图,就是求到u,v的最短距离

时间复杂度为O(n+q),n为树的节点数,q为询问次数
离线算法

Trajan算法基于深度优先搜索,对于新搜索到的一个节点,首先创建由这个节点构成的集合,再对当前节点的每一个子树进行搜索,每搜索完一棵子树,则可确定子树内的LCA询问都已解决。其他的LCA询问的结果必然在这个子树之外,这时把子树所形成的集合与当前节点的集合合并,并将当前节点设为这个集合的祖先。之后继续搜索下一棵子树,直到当前节点的所有子树搜索完。这时把当前节点也设为已被检查过的,同时可以处理有关当前节点的LCA询问。

代码流程:
算法:Tarjan_Dfs(u),对u为根的子树进行DFS
输入:树u和LCA查询集合Q
输出:Q的答案
(1) 创建并查集合u
(2) 对于(u,v)属于Q,如果v已经被标记,则Ancestor(u,v) <-v为v所在集合的根
(3) 对于u的每一个儿子v
1.递归调用Tarjan_Dfs(v)
2.合并u和v所在的集合,设根为u
(4)标记u

const int maxn=10000;//节点数
using namespace std;
int f[maxn],size[maxn],in[maxn],vis[maxn],ance[maxn];//集合,名次,入度,标记,祖先
vector<int> node[maxn],que[maxn];//边表,询问
void Init(){
    for(int i=0;i<maxn;++i){
        node[i].clear();
        que[i].clear();
        size[i]=1;
        f[i]=i;
    }
}
int find(int x){
    return f[x]==x? x:f[x]=find(f[x]);
}
void Union(int a,int b){//按名次合并
    int x=find(a);
    int y=find(b);
    if(size[x]<=size[y]){
        f[x]=y;
        size[y]+=size[x];
    }else{
        f[y]=x;
        size[x]+=size[y];
    }
}
void LCA(int rt){//dfs
    int sz=node[rt].size();
    ance[rt]=rt;//创建集合
    for(int i=0;i<sz;++i){
        LCA(node[rt][i]);//递归调用子树
        Union(rt,node[rt][i]);//将子树与父节点合并
        ance[find(node[rt][i])]=rt;//将子树所在的集合的祖先指向根
    }
    vis[rt]=1;
    sz=que[rt].size();
    for(int i=0;i<sz;++i){
        if(vis[que[rt][i]]){
            printf("%d\n",ance[find(que[rt][i])]);//v所在集合的根
        }
    }
}
int main()
{
    for(int i=0;i<n;++i){
        if(in[i]==0){LCA(i);break;}//找到根节点
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值