最近公共祖先
本文介绍了求最近公共祖先(lca)的三个算法。
- 向上标记法
- 倍增法
- tarjan 算法
同时,给出了倍增法和 tarjan 算法的代码表示。
最近公共祖先是指在有根树中,找出某两个结点 u 和 v 最近的公共祖先,即满足 x 是 u 和 v 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)的一个结点 x。
向上标记法
从其中一个点,向根节点遍历,并把途经的点全部遍历。然后另一个点也开始遍历,如果走到了一个已经被遍历过的点了,那么这个点就是最近的公共祖先。
LCA 算法:
- 如果 b 的位于 a 的下方。那么交换 a 和 b,使得在运行的过程中,a 始终位于 b 的下方。
- 如果 a 和 b 不在同一层,那么,将 a 一直回溯,使得 a 和 b 位于同一层。
- 如果 a 和 b 不一样,则将 a 和 b 一起回溯,直到 a 和 b 相等。这个时候,a (b) 就是 a 和 b 的公共祖先节点。
倍增法
倍增法可以看成是对向上标记法的一个优化,优化了查询父节点的时间。
原理:任何一个数字都可以使用二进制来表示,这里使用二进制来表示当前节点的深度。通过二进制可以将节点查找父节点的时间复杂度从 O(n)
降为 O(logn)
时间复杂度:预处理:O(nlogn)
,查询:O(nlogn)
1 |
|
tarjan 算法
Tarjan 算法是一个离线求 lca 算法。离线的意思是它会先读取所有的查询,然后再运行算法,当算法运行结束的时候,答案也出来了。反之,如果是一个在线算法,那么就是会先读取一个查询然后输出一个查询。离线算法和在线算法之间的区别是是否要实时的输出结果。
时间复杂度是 O(n + m)
,其中,n 是点的个数,m 是询问的数量
本质是对向上标记法的一个优化。基于深度优化遍历,在遍历的过程中,把点分成三大类:
- 已经遍历过的点,已经搜索过,且已经回溯过的点。(点及其子树已经搜完)
- 正在搜索的点(分支)。
- 还未搜到的点。
算法步骤如下:
- 首先,将所有的询问都读入,然后从某个根节点开始进行深度优先搜索。
- 在搜索过程中,给每个节点分配一个时间戳和一个追溯值,表示该节点被访问的顺序和能够到达的最小时间戳的节点。
- 同时,使用并查集维护每个节点所属的集合,初始时每个节点为一个单独的集合。
- 当搜索到一个节点 x 时,检查是否有与 x 有询问关系的节点 y 已经被访问过,如果是,则 y 所在的集合的根节点就是 x 和 y 的最近公共祖先。
- 当搜索完 x 的所有子树后,将 x 和其子树中的所有节点合并到 x 的父节点所在的集合中。
- 最后,输出所有询问的答案。
例题:
题目链接:AcWing 1171. 距离
1 |
|