良心倍增法求lca大佬写的:https://blog.csdn.net/wjh2622075127/article/details/81060586
首先说Lca,中文最近公共祖先,见名思意很容易知道是求树上两个点最近的公共祖先
第一种方法:两个点同时向上走,并把经过的点标记,这样当有一个点走到被标记的点的时候,这个点就是要找的点。
第二种方法:很明显第一种方法是最朴素的,下面来说优于第一种的方法倍增法
因为只学会了最基本的方法,所以说的是没有优化后的倍增法求lca
我只说一些重要的点,其他的细节可以看我发的链接
在这个方法中比较难的点首先要了解一个数组 present[x][i] ,这个数组表示标号为x节点向上跳2^i步的节点,然后要记住一个转化
present[x][i] =present[present[x][i-1]][i-1];这个数组很好理解,就是说这个点跳2^i步到的点就等于这个点跳到2^i-1再跳2^i-1到的
点,为什么要知道这个呢,首先我们这样写是为了可以直接根据前面最初的状态得到这个数组全部的值。比如知道1-n个点的祖先就
能根据公式和着1-n个点的祖先得到1-n个点每个点跳2^1次的距离,依次类推。有人会问为什么要这么做下转到 L1说明
这个方法的流程:(方法和代码参考链接)
1.存表并将present[x][0]给初始化
2.求每个点的深度(bfs,dfs)
3.将present数组的值都给填上
4.我们开始愉快的查询了
(这里要把两个点都提到同一深度)
L1:这里我们说一下为什么要用这个数组,其实这个数组代表的二进制每为,众所周知在计算机中数都是二进制存储,所以一个任何一个十进制数都可以用二进制表示,比如 10= 1010 我们倍增方法就是将每个点能跳2^i次,i<=log2(n),其中n为总共的节点,然后就从最高位开始跳,也就是从i=log2(n)开始跳,比如i=5开始跳,答案是跳10次到最近公共祖先,那么 第一次就是跳10000(二进制)步=16,因为答案是10=1010,所以就跳超了,跳超了的结果就是跳超了,或者两点跳到的点是相同的,我们就不要这位所以我们在i=5时得到的答案是00000,下一步跳到i=4,这是会发现公共祖先不相同,所以我们保留这位就是01000,一直到i=1我们得到 01001,我们得到的答案再加1就是所要的答案。
for (i=log2(n) ; i >= 0; --i) {
if (parents[u][i] != parents[v][i]) {
u = parents[u][i]; //如果不相同就保留这位
v = parents[v][i]; //
}
}
return parents[u][0]; //这步的意思就是上面得到的答案加1