`
kmplayer
  • 浏览: 508682 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

最近公共祖先LCA:Tarjan算法

阅读更多

1,并查集+dfs
对整个树进行深度优先遍历,并在遍历的过程中不断地把一些目前可能查询到的并且结果相同的节点用并查集合并.

2,分类,使每个结点都落到某个类中,到时候只要执行集合查询,就可以知道结点的LCA了。
对于一个结点u.类别有:
以u为根的子树、除类一以外的以f(u)为根的子树、除前两类以外的以f(f(u))为根的子树、除前三类以外的以f(f(f(u)))为根的子树……

类一的LCA为u,类二为f(u),类三为f(f(u)),类四为f(f(f(u)))。这样的分类看起来好像并不困难。
但关键是查询是二维的,并没有一个确定的u。接下来就是这个算法的巧妙之处了。
利用递归的LCA过程。
当lca(u)执行完毕后,以u为根的子树已经全部并为了一个集合。而一个lca的内部实际上做了的事就是对其子结点,依此调用lca.
当v1(第一个子结点)被lca,正在处理v2的时候,以v1为根的子树+u同在一个集合里,f(u)+编号比u小的u的兄弟的子树 同在一个集合里,f(f(u)) + 编号比f(u)小的 f(u)的兄弟 的子树 同在一个集合里…… 
而这些集合,对于v2的LCA都是不同的。因此只要查询x在哪一个集合里,就能知道LCA(v2,x)

还有一种可能,x不在任何集合里。当他是v2的儿子,v3,v4等子树或编号比u大的u的兄弟的子树(等等)时,就会发生这种情况。即还没有被处理。还没有处理过的怎么办?把一个查询(x1,x2)往查询列表里添加两次,一次添加到x1的列表里,一次添加到x2的列表里,如果在做x1的时候发现 x2已经被处理了,那就接受这个询问。(两次中必定只有一次询问被接受).

3,应用:http://acm.pku.edu.cn/JudgeOnline/problem?id=1330
实现代码:
#include<iostream>
#include<vector>
using namespace std;

const int MAX=10001;
int f[MAX];
int r[MAX];
int indegree[MAX];//保存每个节点的入度
int visit[MAX];
vector<int> tree[MAX],Qes[MAX];
int ancestor[MAX];


void init(int n)
{
    for(int i=1;i<=n;i++)
    {

        r[i]=1;
        f[i]=i;
        indegree[i]=0;
        visit[i]=0;
        ancestor[i]=0;
        tree[i].clear();
        Qes[i].clear();
    }

}

int find(int n)
{
    if(f[n]==n)
        return n;
    else
        f[n]=find(f[n]);
    return f[n];
}//查找函数,并压缩路径

int Union(int x,int y)
{
    int a=find(x);
    int b=find(y);
    if(a==b)
        return 0;
    //相等的话,x向y合并
    else if(r[a]<=r[b])
    {
        f[a]=b;
        r[b]+=r[a];
    }
    else
    {
        f[b]=a;
        r[a]+=r[b];
    }
    return 1;

}//合并函数,如果属于同一分支则返回0,成功合并返回1


void LCA(int u)
{
    ancestor[u]=u;
    int size = tree[u].size();
    for(int i=0;i<size;i++)
    {
        LCA(tree[u][i]);
        Union(u,tree[u][i]);
        ancestor[find(u)]=u;
    }
    visit[u]=1;
    size = Qes[u].size();
    for(int i=0;i<size;i++)
    {
        //如果已经访问了问题节点,就可以返回结果了.
        if(visit[Qes[u][i]]==1)
        {
            cout<<ancestor[find(Qes[u][i])]<<endl;
            return;
        }
    }
}


int main()
{
    int cnt;
    int n;
    cin>>cnt;
    while(cnt--)
    {
        cin>>n;;
        init(n);
        int s,t;
        for(int i=1;i<n;i++)
        {
            cin>>s>>t;
            tree[s].push_back(t);
            indegree[t]++;
        }
        //这里可以输入多组询问
        cin>>s>>t;
        //相当于询问两次
        Qes[s].push_back(t);
        Qes[t].push_back(s);
        for(int i=1;i<=n;i++)
        {
            //寻找根节点
            if(indegree[i]==0)
            {
                LCA(i);
                break;
            }
        }
    }
    return 0;
}
分享到:
评论

相关推荐

    LCA (最近公共祖先) Tarjan & 倍增

    LCA Tarjan: 实现原理 理解:离线算法,建好树后再查询,一次DFS 吧所有查询解决完。 时间复杂度:O(n+q); n个点 q次询问 补一下:链式向前星,并查集 ,Tarjan 代码 #include #include #include #include #...

    最近公共祖先LCA(C++版).docx

    * Tarjan 算法:是一种用于寻找 LCA 的算法。该算法的优点是稳定,时间复杂度在 O(n+q) 之间。Tarjan 算法的基本思路是: 1. 任选一个点为根节点,从根节点开始。 2. 合并 v 到 u 上。 3. 寻找与当前点 u 有询问...

    Tarjan 的 LCA 算法

    Tarjan的LCA算法是由Robert Tarjan提出的,它是一种高效地在线性时间内求解树中两个节点的最近公共祖先的方法。这个算法的关键在于对树进行深度优先搜索(DFS)的同时,利用一个叫做“下沉路径”的概念来构建一个...

    RMQ以及LCA:最近公共祖先

    ### RMQ与LCA:最近公共祖先解析及解法 #### 一、引言 在计算机科学领域,尤其是在算法设计中,“最近公共祖先”(LCA, Least Common Ancestor)和“区间最小值查询”(RMQ, Range Minimum Query)是两个非常重要...

    LCA.zip_lca Tarjan pascal_lca pascal_tarjan lca pascal

    在本例中,提供的压缩包文件"LCA.zip"包含了使用Pascal语言实现的Tarjan算法来解决最近公共祖先问题的代码。 Tarjan算法是由Robert Endre Tarjan设计的一种高效算法,主要用于解决图的深度优先搜索(DFS)和强连通...

    lca求最近公共祖先

    Tarjan算法是由Robert Tarjan提出的一种在线性时间内找到图中所有简单环的算法,但在LCA问题中,它被巧妙地用于寻找最近公共祖先。该算法的核心思想是深度优先搜索结合并查集。 1. **深度优先搜索**:Tarjan算法...

    Tarjan应用LCA

    标题中的“Tarjan应用LCA”指的是在图论和数据结构领域中,使用Tarjan算法来解决最近公共祖先(Least Common Ancestor, LCA)问题。最近公共祖先是指在一棵树中,两个节点的共同祖先中离根节点最远的一个。LCA问题在...

    Tarjan算法

    在图论和计算机科学中,Tarjan算法是由Robert Endre Tarjan设计的一系列高效算法,主要用于解决图的相关问题,包括强连通分量、拓扑排序和最近公共祖先(Lowest Common Ancestor, LCA)等问题。这里的焦点是最近公共...

    Tarjan算法讲义

    Tarjan 算法是图论中非常实用 / 常用的算法之一,能解决强连通分量,双连通分量,割点和桥,求最近公共祖先(LCA)等问题。 关于 Tarjan 算法,笔者将用一系列文章系统介绍 Tarjan 算法的原理以及其主要解决的问题...

    contest_tarjan_LCA_源码

    让我们深入探讨一下Tarjan算法以及如何应用于寻找最近公共祖先。 首先,我们来理解最近公共祖先的概念。在一棵有根树中,两个节点u和v的最近公共祖先(LCA)是指距离根节点最近的那个节点,这个节点同时是u和v的...

    最低公共祖先算法实现

    最低公共祖先(Lowest Common Ancestor,简称LCA)算法是数据结构与算法中的一个重要概念,主要用于处理树形结构的问题,比如在二叉树、树状数组或图中找到两个节点的最近公共祖先。在本项目中,它通过C++语言实现,...

    倍增:C++算法教程(临时保存)

    常见的LCA算法有在线和离线两种,包括预处理每次查询、在线转换为RMQ、在线转换为±1RMQ、Tarjan算法、树链剖分和Link Cut Tree等。这些算法在预处理和查询阶段的时间复杂度各有不同,但都比暴力遍历整棵树的方式要...

    数据结构求公共祖先

    此外,为了优化查询性能,可以采用预处理的方法,如LCA(Lowest Common Ancestor)数据结构,如Morris遍历或Tarjan's offline LCA算法,它们能在O(1)的时间复杂度内求解任意两个节点的最近公共祖先。 总结起来,求...

    算法之LCA与RMQ问题1

    在计算机科学领域,算法是解决问题的关键,而LCA(最近公共祖先)和RMQ(区间最值查询)是树形结构和数组处理中常见的两类问题。本文将详细介绍这两种算法及其高效解决方案。 首先,LCA(最近公共祖先)问题是在...

    RMQ与LCA ACM必备

    LCA,最近公共祖先,是图论中的一个重要概念,通常应用于树状结构。给定树中的两个节点u和v,LCA是指距离u和v最近的共同祖先节点。例如,在一个树形结构中,A是B和C的LCA,D是E和F的LCA。 1. **Tarjan离线算法**: ...

    LCA与RMQ相关题解1

    【LCA(最近公共祖先)】 最近公共祖先(Lowest Common Ancestor,简称LCA)问题在图论和算法中具有重要的地位,特别是在处理树形结构的数据时。LCA问题指的是在一个树形结构中,查找两个指定节点的最近公共祖先,即...

    lca.rar_LCA

    在IT领域,特别是数据结构和算法的设计中,"最近公共祖先"(Lowest Common Ancestor,简称LCA)是一个常见的问题。这个问题出现在许多场景中,比如计算机科学中的树形结构处理,生物信息学中的基因进化分析等。最近...

    3.郭华阳《RMQ与LCA问题》1

    LCA(Lowest Common Ancestor)问题则是针对树形结构的一个经典问题,它要求找出树中两个节点的最近公共祖先。最近公共祖先指的是在树中,同时是两个给定节点的祖先,并且离根节点最近的节点。LCA 问题在图论、数据...

    算法刷题提高阶段-图论9

    在这个“算法刷题提高阶段-图论9”的主题中,我们将深入探讨图论中的一个关键概念——最近公共祖先(Lowest Common Ancestor, LCA)。 最近公共祖先问题通常出现在树形结构的数据中,如文件系统、家族树或计算机...

    ACM中的RMQ和LCA问题

    LCA问题则涉及树的数据结构,寻找两个节点的最近公共祖先。例如,树中的节点A、B、C...L,A和G的最近公共祖先为A,D和K的最近公共祖先为D,而B和T的最近公共祖先为B。解决LCA问题的常见算法是Tarjan离线算法,它使用...

Global site tag (gtag.js) - Google Analytics