【算法模板】图论:最近公共祖先(LCA)

【算法模板】图论:最近公共祖先(LCA)

概念

最近公共祖先(Lowest Common Ancestor,简称LCA)问题通常出现在树或图的结构中,特别是在计算机科学和算法领域。这个问题的核心是找到两个节点在树中的共同祖先,且这个祖先的深度(或者说高度)是最小的。

在算法中,我们通常使用以下步骤来找到两个节点的LCA:

  1. 树的遍历:首先,我们需要遍历整棵树,记录每个节点的深度和父节点信息。这通常通过深度优先搜索(DFS)实现。
  2. 初始化信息:在遍历过程中,我们为每个节点存储它的深度和一系列的父节点。这些父节点可以快速帮助我们跳转到更高级别的祖先。
  3. 对齐深度:当我们需要找到两个节点A和B的LCA时,我们首先确保A在B的上面或者B在A的上面。这通常通过比较它们的深度来实现。
  4. 二进制提升:利用之前存储的父节点信息,我们可以快速跳转到更高的祖先。这就像是使用二进制来加速我们的搜索过程。
  5. 查找LCA:一旦两个节点的深度对齐,我们就可以通过比较它们的父节点来找到共同的祖先。如果父节点相同,我们就继续向上查找;如果不同,我们就沿着树向上跳转,直到找到共同的祖先。
  6. 返回结果:最终,我们找到的共同祖先就是LCA。

模板

cpp 复制代码
struct Tree {
    int n;
    vector<vector<int>> ver, val;
    vector<int> lg, dep;
    Tree(int n) {
        this->n = n;
        ver.resize(n + 1);
        val.resize(n + 1, vector<int>(30));
        lg.resize(n + 1);
        dep.resize(n + 1);
        for (int i = 1; i <= n; i++) { //预处理 log
            lg[i] = lg[i - 1] + (1 << lg[i - 1] == i);
        }
    }
    void add(int x, int y) { // 建立双向边
        ver[x].push_back(y);
        ver[y].push_back(x);
    }
    void dfs(int x, int fa) {
        val[x][0] = fa; // 储存 x 的父节点
        dep[x] = dep[fa] + 1;
        for (int i = 1; i <= lg[dep[x]]; i++) {
            val[x][i] = val[val[x][i - 1]][i - 1];
        }
        for (auto y : ver[x]) {
            if (y == fa) continue;
            dfs(y, x);
        }
    }
    int lca(int x, int y) {
        if (dep[x] < dep[y]) swap(x, y);
        while (dep[x] > dep[y]) {
            x = val[x][lg[dep[x] - dep[y]] - 1];
        }
        if (x == y) return x;
        for (int k = lg[dep[x]] - 1; k >= 0; k--) {
            if (val[x][k] == val[y][k]) continue;
            x = val[x][k];
            y = val[y][k];
        }
        return val[x][0];
    }
    int clac(int x, int y) { // 倍增查询两点间距离
        return dep[x] + dep[y] - 2 * dep[lca(x, y)];
    }
    void work(int root = 1) { // 在此初始化
        dfs(root, 0);
    }
};

例题

【模板】最近公共祖先(LCA)

P3379 【模板】最近公共祖先

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;

const int N=5e5+5;
vector<int> tree[N];
int parent[N][20];
int depth[N];

void dfs(int nowp,int prev){
    depth[nowp]=depth[prev]+1;
    parent[nowp][0]=prev;
    for(int i=1;i<20;i++)
        parent[nowp][i]=parent[parent[nowp][i-1]][i-1];
    for(int next:tree[nowp]){
        if(next==prev)continue;
        dfs(next,nowp);
    }
}

int find(int u,int v){
    if(depth[u]<depth[v])swap(u,v);
    for(int i=19;~i;i--) {
        if (depth[u] - (1 << i) >= depth[v])u = parent[u][i];
    }
    if(u==v)return u;
    for(int i=19;~i;i--){
        if(parent[u][i]==parent[v][i])continue;
        u=parent[u][i],v=parent[v][i];
    }
    return parent[v][0];
}

int main(){
    int n,m,s;cin>>n>>m>>s;
    for(int i=1;i<n;i++){
        int u,v;cin>>u>>v;
        tree[u].push_back(v);
        tree[v].push_back(u);
    }
    dfs(s,0);
    while(m--){
        int u,v;cin>>u>>v;
        cout<<find(u,v)<<endl;
    }
}

最近公共祖先LCA查询

最近公共祖先LCA查询 - 蓝桥云课 (lanqiao.cn)

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;

int main(){
    //-----存树-----
    int n;cin>>n;
    vector<vector<int>> tree(n+1);
    for(int i=1;i<n;i++){
        int u,v;cin>>u>>v;
        tree[u].emplace_back(v);
        tree[v].emplace_back(u);
    }
    //-----初始化-----
    const int t=ceil(log2(n));
    vector<int> depth(n+1);
    vector<vector<int>> fa(n+1,vector<int>(t));
    //-----深度与父节点-----
    function<void(int,int)> dfs=[&](int nowp,int prev){
        depth[nowp]=depth[prev]+1;
        fa[nowp][0]=prev;
        for(int i=1;i<t;i++)fa[nowp][i]=fa[fa[nowp][i-1]][i-1];
        for(int next:tree[nowp]){
            if(next==prev)continue;
            dfs(next,nowp);
        }
    };dfs(1,0);
    //-----lca实现-----
    auto lca=[&](int u,int v){
        if(depth[u]<depth[v])swap(u,v);
        for(int i=t-1;~i;i--)
            if(depth[u]-(1<<i)>=depth[v])u=fa[u][i];
        if(u==v)return u;
        for(int i=t-1;~i;i--){
            if(fa[u][i]==fa[v][i])continue;
            u=fa[u][i],v=fa[v][i];
        }
        return fa[u][0];
    };
    //------查询-----
    int Q;cin>>Q;
    while(Q--){
        int a,b;cin>>a>>b;
        cout<<lca(a,b)<<endl;
    }
    return 0;
}

版本分支

版本分支 - 蓝桥云课 (lanqiao.cn)

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;

int main(){
    int N,Q;cin>>N>>Q;
    vector<vector<int>> tree(N+1);
    for(int i=1;i<N;i++){
        int u,v;cin>>u>>v;
        tree[u].emplace_back(v);
    }
    const int t= ceil(log2(N));
    vector<int> depth(N+1);
    vector<vector<int>>fa(N+1,vector<int>(t));
    queue<int> q;
    q.push(1);
    while(!q.empty()){
        int nowp=q.front();
        q.pop();
        for(int next:tree[nowp]){
            depth[next]=depth[nowp]+1;
            fa[next][0]=nowp;
            for(int i=1;i<t;i++)fa[next][i]=fa[fa[next][i-1]][i-1];
            q.push(next);
        }
    }
    while(Q--){
        int x,y;cin>>x>>y;
        if(x==y){
            puts("YES");
            continue;
        }
        if(depth[y]<=depth[x]){
            puts("NO");
            continue;
        }
        for(int i=t-1;~i;--i)
            if(depth[y]-(1<<i)>=depth[x])
                y=fa[y][i];
        if(x==y)puts("YES");
        else puts("NO");
    }
    return 0;
}
相关推荐
大二转专业35 分钟前
408算法题leetcode--第24天
考研·算法·leetcode
凭栏落花侧41 分钟前
决策树:简单易懂的预测模型
人工智能·算法·决策树·机器学习·信息可视化·数据挖掘·数据分析
hong_zc2 小时前
算法【Java】—— 二叉树的深搜
java·算法
吱吱鼠叔3 小时前
MATLAB计算与建模常见函数:5.曲线拟合
算法·机器学习·matlab
嵌入式AI的盲4 小时前
数组指针和指针数组
数据结构·算法
Indigo_code6 小时前
【数据结构】【顺序表算法】 删除特定值
数据结构·算法
阿史大杯茶7 小时前
Codeforces Round 976 (Div. 2 ABCDE题)视频讲解
数据结构·c++·算法
LluckyYH7 小时前
代码随想录Day 58|拓扑排序、dijkstra算法精讲,题目:软件构建、参加科学大会
算法·深度优先·动态规划·软件构建·图论·dfs
转调7 小时前
每日一练:地下城游戏
开发语言·c++·算法·leetcode
不穿格子衬衫8 小时前
常用排序算法(下)
c语言·开发语言·数据结构·算法·排序算法·八大排序