LCA算法是用于在一个树或者一个图中,找出两个节点的最近的公共祖先。
关于离线算法和在线算法的区别:
对于一个在线算法是指它可以以序列化的方式一个个的处理输入,也就是说在开始时并不需要已经知道所有的输入。如插入排序
对于一个离线算法,在开始时就需要知道问题的所有输入数据,而且在解决一个问题后就要立即输出结果。如选择排序
(选择排序在排序前就需要知道所有待排序元素,然而插入排序就不必。)
第一种方法:
1、先通过DFS算法,对树或图进行预处理,获得一棵树或一个图中所有节点的深度及其父节点。然后根据已知的信息,不断的将两个节点往回迭代,直到找到了同一高度,同一个父节点,此时这个节点便是他们两点的公共祖先了。
int LCA(int u,int v) //在某一个图中,找出u,v的公共祖先
{
if(h[u]>h[v]) // 深度不同,单个节点向上攀爬,到与另一节点同一高度
{
while(h[u]>h[v]) //提示:在这个循环里做文章,就可以解决很多环的问题
{
u=pre[u]; //pre[u]表示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离线算法
该算法思路:从根开始深搜遍历树,每当回溯到一个节点时,那就意味着已经完成了该节点子树的遍历,显然这个节点就是子树中所有点以及其本身的最近公共祖先,以此类推到整个树。对于一个点,只有完成了其子树的遍历,我们才改变其父节点的值,即把子树所形成的集合与当前结点的集合合并,并将当前结点设为这个集合的祖先(运用并查集的思想)。这样,对于每次询问(就是给出两点标号,求两点间最短距离),假设为 A 和 B,我们搜到 A (或B)时,如果 B (或A)已经被访问过,那么这个时候最近公共祖先必然是B(或A)所在集合对应的祖先C,因为我们对A的访问就是从最近公共祖先C转过来的,并且在从C的子树B转向A的时候,我们已经将B的祖先置为了C,同时这个C也是A的祖先,那么C必然是A、B的最近公共祖先。
算法示例:
(1)POJ 1470 题目地址:http://poj.org/problem?id=1470 解答地址:http://blog.csdn.net/geniusluzh/article/details/6609685
(2)HDU2586 题目地址:http://acm.hdu.edu.cn/showproblem.php?pid=2586 解答地址:http://blog.csdn.net/jarily/article/details/8947928