最近公共祖先

最近公共祖先(Lowest Common Ancestor,LCA)是指两个或多个节点在一棵树中共有的祖先节点中深度最低的那个。在算法竞赛中,LCA是一种常用的问题,在树上进行相关操作的时候,LCA可以有效提高算法的效率。

一、基本概念

在树中,从根节点向下走到节点$p$和$q$,假设它们之间任意一条路径上的最后一个公共节点为$x$,则$x$是$p$和$q$的公共祖先。如果一个节点是它自己的祖先,那么它就是公共祖先中深度最低的那个,也就是最近公共祖先。

二、求解LCA的方法

1.暴力枚举法

暴力枚举算法的做法是,对于每一对需要查找LCA的节点$p$和$q$,从$p$依次向上遍历祖先,直到找到某个祖先节点$u$,使得$u$是$q$的祖先。这样,找到的节点$u$就是$p$和$q$的最近公共祖先。这种算法的时间复杂度为$O(n^2)$。

struct TreeNode{
    int val;
    TreeNode* left;
    TreeNode* right;
};
using p = pair;

TreeNode* LCA(TreeNode* root, TreeNode* p, TreeNode* q){
    function isAncestor = [](TreeNode* p, TreeNode* q){
        if(p == nullptr) return false;
        if(p == q) return true;
        return isAncestor(p->left, q) || isAncestor(p->right, q);
    };
    queue

q; q.push({root, -1}); vector

tmp; while(!q.empty()){ int n = q.size(); while(n--){ auto [cur, parent] = q.front(); q.pop(); tmp.emplace_back(cur, parent); if(cur->val == p->val || cur->val == q->val){ if(tmp.size() == 1) return cur; break; } if(cur->left){ q.push({cur->left, cur->val}); } if(cur->right){ q.push({cur->right, cur->val}); } } if(isAncestor(tmp.back().first, p) && isAncestor(tmp.back().first, q)) continue; while(tmp.size()){ auto [cur, _] = tmp.back(); tmp.pop_back(); if(isAncestor(cur, p) && isAncestor(cur, q)) return cur; } } return nullptr;}

2. 深度优先搜索法

在对树进行深度优先搜索(DFS)的时候,记录下每个节点的深度和父节点,如果需要查找节点$p$和$q$的LCA,对于节点$p$和$q$,沿着树分别向上找到它们的祖先节点,一直找到它们的深度相等,这样的节点就是它们的最近公共祖先。

// 建立 parent 和 depth 数组
void dfs(int u, int p, int d){
    parent[u] = p;
    depth[u] = d;
    for(auto v : g[u]){
        if(v != p){
            dfs(v, u, d + 1);
        }
    }
}

// 求解LCA
int LCA(int u, int v){
    int du = depth[u];
    int dv = depth[v];
    while(du > dv){
        u = parent[u];
        du--;
    }
    while(dv > du){
        v = parent[v];
        dv--;
    }
    while(u != v){
        u = parent[u];
        v = parent[v];
    }
    return u;
}

3.倍增法

针对较大规模的树,在求解LCA时暴力枚举的时间复杂度太高,可采用倍增法。倍增法的思路是,预处理出每个节点往上跳$2^k$个节点后能到达的节点,从而减少查找LCA的时间复杂度,使之变为$O(logn)$。具体实现时,需要预处理一个$fa[i][j]$数组,表示从节点$i$开始往上跳$2^j$个节点所能到达的节点编号。同时,还需要预处理一个$depth[i]$数组,表示节点$i$的深度。

// 节点编号从`0`开始
const int MAXN = 1e5 + 10;
int depth[MAXN], fa[MAXN][21];

// 预处理深度和预处理fa数组
void dfs(int u, int p, int d){
    depth[u] = d;
    fa[u][0] = p;
    for(int i = 1; (1 << i) < MAXN; ++i){
        fa[u][i] = fa[fa[u][i - 1]][i - 1];
    }
    for(auto v : g[u]){
        if(v != p){
            dfs(v, u, d + 1);
        }
    }
}

// 求解LCA
int LCA(int u, int v){
    if(depth[u] = 0; --i){
        if(depth[u] - (1 <= depth[v]){
            u = fa[u][i];
        }
    }
    if(u == v) return u;
    for(int i = log2(depth[u]); i >= 0; --i){
        if(fa[u][i] != fa[v][i]){
            u = fa[u][i];
            v = fa[v][i];
        }
    }
    return fa[u][0];
}

三、总结

求解树上两个节点的最近公共祖先是树相关算法中的重要问题。根据不同的应用场景和需求,可以选择不同的LCA算法,如暴力枚举、深度优先搜索和倍增法等。在实际应用中,需要根据数据规模和时间复杂度的要求,选择合适的算法。

原创文章,作者:RZPJY,如若转载,请注明出处:https://www.506064.com/n/368565.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
RZPJYRZPJY
上一篇 2025-04-12 01:13
下一篇 2025-04-12 01:13

相关推荐

  • 教你如何联机我的世界(我的世界怎么和好友联机)

    最近老是有小伙伴们问我是怎么联机一起玩的。今天我就给大家说一说是怎么联机的,这样大家就可以和自己的基友和小伙伴们一起耍了。 联机的方法有两种选择:一是可以在自己的局域网里面玩耍,二…

    游戏 2025-02-08
  • 账号注册步骤详解(如何steam账号注册手机)

    最近我们游戏风云的“风云小店”开张了,新店优惠哟~卖的比steam还要便宜,不过不少的小伙伴是第一次接触steam,对注册账号还有些困惑?就随着小编我的文章…

    游戏 2025-02-08
  • 最近公共祖先详解

    一、定位概念 最近公共祖先(LCA),指的是在一个树或有向无环图中,找到两个节点的共同祖先中深度最小的一个。 LCA问题是计算机科学中一种常见的算法问题,它有着广泛的应用。例如,可…

    编程 2025-01-21
  • java显示没法启动该程序(java无法启动选择的项,最近未进行任何启动)

    本文目录一览: 1、java 无法启动该应用程序,如何解决? 2、JAVA无法运行怎么办 3、java无法启动该应用程序jnlp文件打不开 4、如图示,打开一款JAVA应用程序,系…

    编程 2025-01-14
  • 查看最近登录用户的历史记录

    一、介绍 在Linux系统中,可以通过查看最近登录用户的历史记录来追踪系统的活动,这对于运维工程师来说非常重要。本文将介绍如何查看最近登录用户的历史记录,以及如何对该记录进行分析。…

    编程 2025-01-14
  • 草图大师最近文件怎删除,草图大师批量删除

    本文目录一览: 1、sketchup中怎么删除多余的模板 2、谁知道草图大师的组件怎样一起删除? 只留下需要的 急用 谢谢 3、su怎么删除自己创建的材质文件夹? 4、草图大师模型…

    编程 2025-01-09
  • SQL Server查询最近执行的SQL

    一、查询最近执行的SQL语句 查询最近执行的SQL语句可以通过系统视图sys.dm_exec_query_stats来实现,该视图存储了SQL Server最近执行的查询信息,包括…

    编程 2025-01-09
  • java去最近的时间(java要多久)

    本文目录一览: 1、java按照时间查询,获取近1月时间信息。时间如何加减?简单易懂,谢谢了。 2、在java里怎么取离当前日期最近的一个星期天?? 3、java中一个List里放…

    编程 2025-01-04
  • 正在学python中,我最近在学Python

    本文目录一览: 1、正在学习python,教程中有两句理解不了,请大神指教。 2、现在学python怎么样 有前景吗 3、自学Python过程中应注意哪些问题 正在学习python…

    编程 2024-12-26
  • 最近学习python量化,量化python怎么学

    本文目录一览: 1、学Python怎么样,前景怎么样? 2、Python量化教程:不得不学的K线图「代码复制可用」 3、普通人为什么要学习Python 4、python量化交易半个…

    编程 2024-12-12

发表回复

登录后才能评论