LCA

LCA算法:

LCA(Least Common Ancestor),顾名思义,是指在一棵树中,距离两个点最近的两者的公共节点。也就是说,在两个点通往根的道路上,肯定会有公共的节点,我们就是要求找到公共的节点中,深度尽量深的点。还可以表示成另一种说法,就是如果把树看成是一个图,这找到这两个点中的最短距离。

      LCA算法有在线算法也有离线算法,所谓的在线算法就是实时性的,比方说,给你一个输入,算法就给出一个输出,就像是http请求,请求网页一样。给一个实时的请求,就返回给你一个请求的网页。而离线算法则是要求一次性读入所有的请求,然后在统一得处理。而在处理的过程中不一定是按照请求的输入顺序来处理的。说不定后输入的请求在算法的执行过程中是被先处理的。

      本文先介绍一个离线的算法,就做tarjan算法。这个算法是基于并查集和DFS的。Dfs的作用呢,就是递归,一次对树中的每一个节点进行处理。而并查集的作用就是当dfs每访问完(注意,这里是访问完)到一个点的时候,就通过并查集将这个点,和它的子节点链接在一起构成一个集合,也就是将并查集中的pnt值都指向当前节点。这样就把树中的节点分成了若干个的集合,然后就是根据这些集合的情况来对输入数据来进行处理。

      比方说当前访问到的节点是u,等u处理完之后呢,ancestor[u]就构成了u的集合中的点与u点的LCA,而ancestor[fa[u]]就构成了,u的兄弟节点及其兄弟子树的集合中点与u的LCA,而ancestor[fa[fa[u]]]就构成了u的父亲节点的兄弟节点及其兄弟子树的集合中的点与u的LCA。然后依次类推,这样就构成了这个LCA的离线算法。

 

下面来分析一下代码:

int findp(int x)

{

    if(pnt[x]!=x)   pnt[x] = findp(pnt[x]);

    return pnt[x];

}                            

int unionset(int x,int y)

{

    int x = findp(x);

    int y = findp(y);

    pnt[y] = x;

}      //以上两步是并查集的操作,没啥过多解释。

void Lcancestor(int parent)

{

    pnt[parent] = parent;   //当访问到一个点的时候,先将其自己形成一个集合

    ancestor[findp(parent)] = parent; 

    for(int i=0;i<=child[parent].size();i++)

    {     //接着一次访问节点的子节点,

        Lcancestor(child[parent][i]);   //依次对子节点进行访问。

        unionset(parent,child[parent][i]);   //在处理完后,将子节点的集合链接到父节点

        ancestor[findp(child[parent][i])] = parent;

    }   //实际上这一步起到了并查集的压缩节点的作用。这样可以将查询降低到O(1)

    color[parent] = true;

    if( parent = first && color[second] )     //这里的first和second主要针对的是查询的每次操作时输入的两个数。

    {

        ans = ancestor[findp(second)] ;

    }

    if( parent = second && color[first] )

    {

        ans = ancestor[findp(first)];

    }

}

LCA还有其他的算法,例如,将每个点到根节点的路径构成一个链表,那么LCA就是求两个链表的公共节点中位置最靠后的一个点。还有的LCA可以与RMQ问题结合起来,至于什么事RMQ问题,将会在下一篇博文中给出解释。
     

你可能感兴趣的:(LCA)