【算法】LCA的三种算法

这篇具有很好参考价值的文章主要介绍了【算法】LCA的三种算法。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

什么是LCA?

LCA(Least Common Ancestors),即最近公共祖先,是指在有根树中,找出某两个结点x和y最近的公共祖先。

三种算法

用三种算法可以求解LCA问题,分别为朴素算法、倍增算法和Tarjan算法。

朴素算法

倍增算法和Tarjan算法都在建立在朴素算法的思想下,因此,了解朴素算法的思想有助于更好的理解进阶算法。
  朴素算法前置知识:邻接表,dfs。
  假设我们要寻找某两个节点x和y的LCA,那么我们肯定是让深度更深的那个结点跳到另一个结点深度处,然后再让这两个结点一起向上跳,直到首次相遇。
  光说可以有些抽象,举个例子,就以下面这张图中的树为例。lca算法,算法,深度优先
  图中结点4先跳到结点3的位置,然后两个结点一起向上跳,随后跳到结点1处相遇,所以结点2和结点4的LCA为结点1。
  也就是说,我们只要记录下每个结点的深度信息和祖先信息,就能通过逐个向上跳跃直至相遇来确定两个结点的LCA。
  这便是LCA朴素算法的核心支撑所在,但是由于朴素算法每次跳跃一层,因此他的时间复杂度很差,尤其是当树退化为链的时候,那么如果我们让他跳跃多层,是不是可以更好更快的解决问题呢?答案是一定的,而接下来的倍增算法就是这个思想。

倍增算法

倍增算法前置知识:邻接表,DP&倍增,dfs。
  倍增算法我们将会定义一个数组fa[i][j]表示结点i向上跳2j层所到的结点,从而实现了倍增跳跃,而且,通过有限的组合跳跃,我们到达任意结点处。例如向上5层,我们可以先向上跳跃22层之后,再向上跳跃2^0次方。同时,我们可以证明又fa[x][i] = fa[fa[x][i-1]][i-1],实质就是2^(i-1) + 2^(i-1) = 2^i.
  同时,倍增LCA算法中还用到了贪心的思想,假如现在有两个结点x,y,假设x深度更大,则我们要尽可能地让x在不超过y的深度的前提下,尽可能地接近y,也就是跨的步子尽可能大!这样操作过后,结点x与结点y的深度就一定相同了。
  相同之后,如果已经重合,直接return,如果没有,那么现在两个结点处于一个平行的位置。接下来我们让两个结点同时向上跳,也是能跳多大就跳多大,但是肯定是有限制的,就像上一步一样,这个限制就是只有在跳完之后他们结点不重合时才跳。这个地方有点绕,不要急,我们结合代码看一下。

点击查看代码
int lca(int u, int v) {
    if(depth[u] < depth[v]) swap(u, v);
    for(int i = MAXLOG - 1; i >= 0; i--) {
        if(depth[u] - (1 << i) >= depth[v]) {
            u = parent[u][i];
        }
    }
    if(u == v) return u;
// 注意看这里,这一步是平行之后的操作
    for(int i = MAXLOG - 1; i >= 0; i--) {
        if(parent[u][i] != parent[v][i]) {
            u = parent[u][i];
            v = parent[v][i];
        }
    }
    return parent[u][0];
}
  我们注意到,在平行之后,只要在parent[u][i] != parent[v][i]的前提下尽可能地大跨步就能保证跳到最近的公共祖先的前一个位置,这个位置上面一层,也就是2^0方层就是公共祖先,同时也是最近的公共祖先。

Tarjan算法

Tarjan算法前置知识:邻接表,并查集,dfs。
  Tarjan巧妙地将并查集和dfs结合在一起,实现了将单次查询地时间复杂度降到了常数级别的离线算法!
  具体步骤如下:

  1. 对于每个节点u,初始化u的祖先为u本身,并标记u为已访问。
  2. 对于每条边(u,v):
        * 如果v未被访问,则对v进行深度优先搜索,并将v的祖先设置为u。
        * 如果v已经被访问,那么u和v的最近公共祖先就是u的祖先和v的祖先的最小值。
  3. 在查询LCA问题时:
        * 对于每个查询(q, u, v),其中q为查询编号,u和v分别为查询的两个节点:
          * 如果(u,v)已经被访问过,那么查询结果为(u,v)的最近公共祖先。
          * 如果(u,v)未被访问过,那么查询结果为(u,v)的最近公共祖先的祖先。

总结

离线用Tarjan,在线用倍增。:)文章来源地址https://www.toymoban.com/news/detail-841585.html

到了这里,关于【算法】LCA的三种算法的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 求解 LCA の方法

    最近公共祖先简称 LCA(Lowest Common Ancestor)。两个节点的最近公共祖先,就是这两个点的公共祖先里面,离根最远的那个。 -----oi wiki 举个例子 在这张图中, (5) 和 (9) 的最近公共祖先就是 (3) , (9) 和 (7) 的最近公共祖先就是 (2) 由于在树上两点间的简单路径是唯一的

    2024年02月02日
    浏览(47)
  • 最长公共子序列LCA

    题目链接:3692. 最长连续公共子序列 - AcWing题库

    2024年02月13日
    浏览(39)
  • 【树上差分+LCA】篮球杯 砍树

    省赛的题现在来补 感觉什么都不会,已经要没了 题意: 思路: 考虑一条边,两端有两棵子树 有这样的性质: 这条边两端的结点的经过次数==M  因此每加一个点对,都对其路径+1 s[u]==M时,与该点连着的边就是合法边了,统计合法边的最大id就行 Code:

    2024年02月06日
    浏览(52)
  • LCA——ST表+欧拉序

    了解到一个quan新的东西: 用ST表(欧拉序)实现LCA(树上最近公共祖先) 前序遍历得到的序列,叫dfs序 但数字可以重复出现,一进一出,叫欧拉序 会发现根结点总在中间 而根结点是该段序列深度最小的点 因此两个点的LCA, 就是在 该序列上两个点第一次出现的区间内深度最小

    2024年02月02日
    浏览(32)
  • AcWing1171. 距离(lca&&tarjan)

    输入样例1: 输出样例1: 输入样例2: 输出样例2:

    2024年02月14日
    浏览(36)
  • LeetCode 周赛 341 场,模拟 / 树上差分 / Tarjan 离线 LCA / DFS

    本文已收录到 AndroidFamily,技术和职场问题,请关注公众号 [彭旭锐] 提问。 大家好,我是小彭。 上周末有单双周赛,双周赛我们讲过了,单周赛那天早上有事没参加,后面做了虚拟竞赛,然后整个人就不好了。前 3 题非常简单,但第 4 题有点东西啊,差点就放弃了。最后,

    2023年04月20日
    浏览(42)
  • 根据基站位置区识别码(LCA)和小区识别(CI)获取经纬度

    在网络攻击溯源时,需要对攻击者的位置进行定位。 已知攻击者发送攻击报文的接入基站的位置区识别码(LCA)和小区识别(CI)码 获取攻击者位置 request 调用API查询经纬度位置 openpyxl 读取 excel 表格 sqlite3 读写数据库 json 数据解析

    2024年02月09日
    浏览(55)
  • 第三章 图论 No.8最近公共祖先lca, tarjan与次小生成树

    O ( m l o g n ) O(mlogn) O ( m l o g n ) ,n为节点数量,m为询问次数,lca是一种在线处理询问的算法 自己也是自己的祖先 倍增: f a ( i , j ) fa(i, j) f a ( i , j ) 表示从i开始,向上走 2 j 2^j 2 j 步走到的点 j = 0,走到父节点 j 0,分两步走,先走到 2 j − 1 2^{j-1} 2 j − 1 步再走 2 j − 1 2^{

    2024年02月13日
    浏览(44)
  • 11.动态规划:树形DP问题、树上最大独立集、树上最小支配集、换根DP、树上倍增(LCA)【灵神基础精讲】

    回溯和树形DP的区别(什么时候需要return结果?):对于回溯,通常是在「递」的过程中增量地构建答案,并在失败时能够回退,例如八皇后。对于递归,是把原问题分解为若干个相似的子问题,通常会在「归」的过程中有一些计算。如果一个递归能考虑用记忆化来优化,就

    2024年02月04日
    浏览(42)
  • 动态维护直径 || 动态维护树上路径 || 涉及LCA点转序列 || 对欧拉环游序用数据结构维护:1192B

    https://www.luogu.com.cn/problem/CF1192B 对于直径的求法,常用dp或两次dfs,但如果要动态维护似乎都不太方面,那么可以维护树上路径最大值。 树上路径为: d e p u + d e p v − 2 × d e p l c a ( u , v ) dep_u+dep_v-2times dep_{lca(u,v)} d e p u ​ + d e p v ​ − 2 × d e p l c a ( u , v ) ​ 为方便求 l c

    2024年02月11日
    浏览(30)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包