最近公共祖先 (LCA) 模板

定义

在树中两个节点的最近公共祖先为:两个点的公共祖先里面离根最远的那个。

查询次数较少的情况

可以通过两次 d f s dfs dfs 解决:第一次 d f s dfs dfs 将根节点到其中一个节点的路径上的节点染色,第一次 d f s dfs dfs 遍历到的最后一个染色节点即为最近公共祖先,时间复杂度为 O ( n ) O(n) O(n)。

查询次数较多的情况

可以通过一遍 d f s dfs dfs 预处理求出各节点 i i i 所在的层数 h [ i ] h[i] h[i],和倍增数组 p a r par par : p a r [ i ] [ j ] par[i][j] par[i][j] 为与 i i i 距离为 2 j 2^j 2j 的祖先节点。然后设查询的两个节点为 u u u 和 v v v,设 h [ u ] ≥ [ v ] h[u]\ge[v] h[u]≥[v] ,若 h [ u ] > h [ v ] h[u]>h[v] h[u]>h[v] 则先将 u u u 上移至和 v v v 同一层。之后 u u u 和 v v v 处于同一层, 此时若 u u u 和 v v v 为同一节点,则 v v v 即为最近公共祖先;否则将 u u u 和 v v v 同时尽可能地上移且保证移动后的 u u u 和 v v v 为不同的节点,无法移动之后 u u u 和 v v v 的父节点即为最近公共祖先。预处理的时间复杂度为 O ( n log n ) O(n\text{log}n) O(nlogn),单次查询的时间复杂度为 O ( log n ) O(\text{log}n) O(logn)。求 l c a lca lca 的代码如下:

cpp 复制代码
int lca(int p, int q) {//这里用整数代表节点,若存在相同值的节点则用节点指针
    if (h[p] < h[q])
        swap(p, q);
    for (int dis = h[p] - h[q], i = 0; dis; i++, dis >>= 1)
        if (dis & 1)
            p = par[p][i];
    if (p == q)
        return p;
    for (int i = i0; i >= 0; i--)// i的初值i0可设为ceil(logn)
        if (par[p][i] != par[q][i]) {
            p = par[p][i];
            q = par[q][i];
        }
    return par[p][0];
}

完整的例子包括生成倍增数组等操作可以参考 二叉树的最近公共祖先 这道题的倍增做法:

cpp 复制代码
class Solution {
public:
    TreeNode *lowestCommonAncestor(TreeNode *root, TreeNode *p, TreeNode *q) {
        unordered_map<TreeNode *, vector<TreeNode *>> par;
        unordered_map<TreeNode *, int> h;
        function<void(TreeNode *, TreeNode *, int)> dfs = [&](TreeNode *root, TreeNode *p, int lev) {
            if (!root)
                return;
            h[root] = lev;
            par[root] = vector<TreeNode *>(18);
            par[root][0] = p;
            for (int i = 1; i < 18; i++)
                par[root][i] = par[par[root][i - 1]][i - 1];
            dfs(root->left, root, lev + 1);
            dfs(root->right, root, lev + 1);
        };
        dfs(root, root, 0);
        if (h[p] < h[q])
            swap(p, q);
        for (int dis = h[p] - h[q], i = 0; dis; i++, dis >>= 1)
            if (dis & 1)
                p = par[p][i];
        if (p == q)
            return p;
        for (int i = 17; i >= 0; i--)
            if (par[p][i] != par[q][i]) {
                p = par[p][i];
                q = par[q][i];
            }
        return par[p][0];
    }
};
相关推荐
Victory_orsh7 分钟前
“自然搞懂”深度学习(基于Pytorch架构)——010203
人工智能·pytorch·python·深度学习·神经网络·算法·机器学习
CoovallyAIHub15 分钟前
突破360°跟踪极限!OmniTrack++:全景MOT新范式,HOTA指标狂飙43%
深度学习·算法·计算机视觉
煤球王子22 分钟前
学而时习之:C++中的字符串
c++
得物技术43 分钟前
得物管理类目配置线上化:从业务痛点到技术实现
后端·算法·数据分析
go_bai1 小时前
Linux--进程池
linux·c++·经验分享·笔记·学习方法
QT 小鲜肉1 小时前
【QT/C++】Qt网络编程进阶:UDP通信和HTTP请求的基本原理和实际应用(超详细)
c语言·网络·c++·笔记·qt·http·udp
CoovallyAIHub1 小时前
首个大规模、跨模态医学影像编辑数据集,Med-Banana-50K数据集专为医学AI打造(附数据集地址)
深度学习·算法·计算机视觉
熬了夜的程序员1 小时前
【LeetCode】101. 对称二叉树
算法·leetcode·链表·职场和发展·矩阵
却道天凉_好个秋2 小时前
目标检测算法与原理(二):Tensorflow实现迁移学习
算法·目标检测·tensorflow
实心儿儿2 小时前
C++ —— list
开发语言·c++