lca算法是用于在一个树或者一个图中,找出两个节点的公共祖先的算法。
有什么用呢?举一个例子:在一棵树上,当我们随便添加一条边的时候,那么就会在原来的树上形成环。如何把这个环找出来呢?嘿嘿,lca算法便是了!
lca算法是运用需要一个预热的过程,简单的说,就是他需要一棵树或者一个图中节点的每个点的深度和父节点。
呵呵,简单啊,DFS就是了。
接下来根据已知的信息,不断的将两个节点往回迭代,直到找到了同一高度,同一个父节点。这个节点便是他们两点的公共祖先了,而这个过程经过的边加起来,便是我们要找的环!
1、DFS(略) 2、 Int lca(int u,int v) //lca算法,在某一个图中,找出u,v的公共祖先 { if(h[u]>h[v]) // 深度不同,单个节点向上攀爬,到与另一节点同一高度 { while(h[u]>h[v]) //提示:在这个循环里做文章,就可以解决很多环的问题 { u=pre[u]; } } else if(h[v]>h[u]) { while(h[v]>h[u]) { v=pre[v]; } } while(u!=v) //深度相同了,同时向上攀爬,直到成了同一点 { u=pre[u]; v=pre[v]; } return u; //找到了公共祖先 }
下面是tarjan离线算法实现LCA
自己看的,不好意思
int find(int x) //并查集 { if(x!=pre[x]) { pre[x]=find(pre[x]); //路径压缩 } return pre[x]; } int lca(int u,int f) //当前节点,父节点 { pre[u]=u; //设立当前节点的集合 for(node *p=link[u];p;p=p->next) { if(p->v==f) continue; lca(p->v,u); //搜索子树 pre[p->v]=u; //合并子树 } v[u]=1; //以u点为集合的点搜索完毕 if(u==A && v[B]==1) { printf("%d\n",pre[find(B)]); } else if(u==B && v[A]==1) { printf("%d\n",pre[find(A)]); } return 0; }
Tarjan算法基于深度优先搜索的框架,对于新搜索到的一个结点,首先创建由这个结点构成的集合,再对当前结点的每一个子树进行搜索,每搜索完一棵子树,则可确定子树内的LCA询问都已解决。其他的LCA询问的结果必然在这个子树之外,这时把子树所形成的集合与当前结点的集合合并,并将当前结点设为这个集合的祖先。之后继续搜索下一棵子树,直到当前结点的所有子树搜索完。这时把当前结点也设为已被检查过的,同时可以处理有关当前结点的LCA询问,如果有一个从当前结点到结点v的询问,且v已被检查过,则由于进行的是深度优先搜索,当前结点与v的最近公共祖先一定还没有被检查,而这个最近公共祖先的包涵v的子树一定已经搜索过了,那么这个最近公共祖先一定是v所在集合的祖先。