定义
一个点的祖先:从该节点出发, 一路向上走能碰到的就是其祖先了
两个点的公共祖先: 就是同一棵树上两个节点的祖先集合中的交集
最近公共祖先就是这个交集里面最靠下的
注意到以上的表述中经常出现向上或靠下等字眼, 说明求最近公共祖先的算法肯定与求节点的高度有关
朴素算法法求LCA
想象一下这个过程:
两个节点先跳到同一个高度
如果两节点相遇 (即原先两个节点存在祖孙关系), 该点即为LCA, 退出
否则, 一起向上跳, 直到相遇
优化
以上这种一层一层跳的方法太慢了, 面对一棵巨大的树时会跑得巨慢, 我们要尝试优化这个过程
试想一下: 如果用倍增跳呢? 是不是速度立刻就上去了?
一些前置工作
- 用倍增的思想求出节点 $x$ 往上跳 $2^p$ 步后可以到达的节点, 存在 $fa[x][p]$ 中
- 用 $\texttt{BFS}$ 或 $\texttt{DFS}$ 求出节点 $x$ 的祖先, 存在 $deep[x]$ 中
这样, 每次跳的时候, 可以通过比较 $deep[a]$ 是否等于 $deep[b]$ 来判断是否在同一层, 并增加跳的长度, 把朴素算法的时间复杂度优化到 $log$ 级别
具体实现
从根节点遍历整棵树, 顺便记录一下子节点的信息
1 | node nod = que.front(); que.pop(); |
开始愉快地跳跃
先跳到同一高度
Code 1
2
3
4
5
6
7
8
9
10
11if (deep[a] < deep[b])
swap(a, b);
if (!deep[b])
return b;
for (int i = MAXP; i >= 0; i--)
{
if (deep[fa[a][i]] >= deep[b])
a = fa[a][i];
if (a == b)
return a;
}两个点一起往上跳
Code 1
2
3
4
5
6
7
8
9for (int i = MAXP; i >= 0; i--)
{
if (fa[a][i] != fa[b][i])
{
a = fa[a][i];
b = fa[b][i];
}
}
return fa[a][0];
上代码
1 |
|
时间复杂度
$$\Theta{(n + nlog_2n + mlog_2n)} = \Theta{((n + m)logn)}$$