`
kmplayer
  • 浏览: 512446 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

最近公共祖先LCA:RMQ转化

阅读更多
1,最近公共祖先(LCA):
对于有根树T的两个结点u、v,最近公共祖先LCA(T,u,v)表示一个结点x,满足x是u、v的祖先且x的深度尽可能大。
2,LCA问题向RMQ问题的转化方法:(RMQ返回最值的下标)
对树进行深度优先遍历,每当“进入”或回溯到某个结点时,将这个结点的深度存入数组dfsNum最后一位。同时记录结点i在数组中第一次出现的位置(事实上就是进入结点i时记录的位置),记做first[i]。如果结点dfsNum[i]的深度记做depth[i],易见,这时求LCA(u,v),就等价于求 E[RMQ(depth,first[u],first[v])],(first[u]<first[v])。
例如:



(深度遍历)difNum[i]为:1,2,1,3,4,3,5,3,1
first[i]为:0,1,3,4,6
(个点对应的深度)depth[i]为:0,1,0,1,2,1,2,1,0

于是有:
LCA(4,2) = difNum[RMQ(D,first[2],first[4])] = difNum[RMQ(D,1,6)] = difNum[2] = 1

转化后得到的数列长度为树的结点数*2-1(每经过一条边,都会记录端点,有N-1条边,所以会回溯N-1次。且每个顶点都会被添加一次,所以长度为2N-1)

3,实例代码:
#include<iostream>
#include<vector>
#include<cmath>
#include<algorithm>
using namespace std;

const int maxn=20010;

struct node //可以添加额外的信息
{
    int v;//孩子结点
};

//注意vector在树问题中的使用
vector<node> tree[maxn];

int dfsnum[maxn]; //记录遍历的节点
int depth[maxn]; //记录节点对应的深度
int first[maxn]; //记录结点第一次访问到时的下标
int top; //记录总的步伐数

void dfs(int m,int f,int dep) //当前节点编号,父节点编号,深度
{
    dfsnum[top]=m;
    depth[top]=dep;
    first[m]=top;
    top++;
    for(unsigned i=0;i<tree[m].size();i++)
    {
        if(tree[m][i].v==f)
            continue;
        dfs(tree[m][i].v,m,dep+1);
        dfsnum[top]=m; //注:每条边回溯一次,所以top的值=n+n-1
        depth[top]=dep;
        top++;
    }
}

int dp[maxn][18];
void makeRmqIndex(int n,int b[]) //返回最小值对应的下标
{
    int i,j;
    for(i=0;i<n;i++)
        dp[i][0]=i;
    for(j=1;(1<<j)<=n;j++)
        for(i=0;i+(1<<j)-1<n;i++)
            dp[i][j]=b[dp[i][j-1]] < b[dp[i+(1<<(j-1))][j-1]]? dp[i][j-1]:dp[i+(1<<(j-1))][j-1];
}
int rmqIndex(int s,int v,int b[])
{
    int k=(int)(log((v-s+1)*1.0)/log(2.0));
    return b[dp[s][k]]<b[dp[v-(1<<k)+1][k]]? dp[s][k]:dp[v-(1<<k)+1][k];
}

int lca(int x,int y)
{
    return dfsnum[rmqIndex(first[x],first[y],depth)];
}

int main()
{
    int n=5;//顶点数
    top=0;
    //分别存放每条边的端点
    int x[]={1,1,3,3};
    int y[]={2,3,4,5};
    node temp;
    for(int i=0;i<n-1;i++) //n-1条边
    {
        temp.v=y[i];
        tree[x[i]].push_back(temp);
        temp.v=x[i];
        tree[y[i]].push_back(temp);
    }
    dfs(1,-1,0); //根节点为1
    cout<<"总数:"<<top<<endl;
    makeRmqIndex(top,depth);
    cout<<"dfsnum:";
    for(int i=0;i<top;i++)
    {
        cout<<dfsnum[i]<<" ";
    }
    cout<<endl;
    cout<<"depth:";
    for(int i=0;i<top;i++)
    {
        cout<<depth[i]<<" ";
    }
    cout<<endl;
    cout<<"first:";
    for(int i=1;i<=n;i++)
    {
        cout<<first[i]<<" ";
    }
    cout<<endl;
    cout<<"lca(4,5):"<<lca(4,5)<<endl;
    cout<<"lca(2,4):"<<lca(2,4)<<endl;
    cout<<"lca(1,4):"<<lca(1,4)<<endl;
    return 0;
}
  • 大小: 3.1 KB
分享到:
评论

相关推荐

    lca求最近公共祖先

    ### LCA求最近公共祖先详解 #### 一、引言 在计算机科学中,最近公共祖先(Least Common Ancestor,简称LCA)问题是图论和数据结构中的一个重要问题,尤其在处理树形结构数据时非常关键。LCA问题通常表述为:给定...

    打萎的RMQ 和 LCA

    RMQ(Range Minimum/Maximum Query,区间最值查询)和LCA(Least Common Ancestor,最近公共祖先)是数据结构和算法中的重要概念,它们在解决实际问题中有着广泛的应用。下面将详细介绍这两种算法的基本概念、经典...

    LCA问题归约成RMQ求解

    LCA代表最远公共祖先(Least Common Ancestor),而RMQ代表范围最小值查询(Range Minimum Query)。这两个概念常用于树形结构和数组处理。 首先,最远公共祖先(LCA)问题是在给定一棵树T和两个节点u、v的情况下,...

    3.郭华阳《RMQ与LCA问题》1

    另一方面,也可以将 LCA 问题转化为 RMQ 问题,通过对树进行某种编码,将查询两个节点的最近公共祖先转化为在编码后的数组中寻找最小值。 **RMQ 与 LCA 问题的解决** 解决 RMQ 问题有多种方法,如: 1. **Spare ...

    LCA.rar_LCA

    在压缩包中的"最近公共祖先(LCA)(转化为RMQ线段树查询)"文件可能包含了具体实现的代码或者详细的步骤解释,供学习者参考。 总的来说,将LCA问题转化为RMQ问题,利用线段树解决,是算法设计中的一种高效策略,它能够...

    RMQ & LCA 问题及其相互关系

    LCA问题是指在有根树中找到两个节点的最近公共祖先。这个问题在树状数据结构的许多高级应用中都扮演着核心角色,如后缀树的构建和查询等。 **基本算法** - **分块技术**:类似于RMQ中的分块策略,LCA也可以通过将...

    算法合集之RMQ与LCA问题PPT学习教案.pptx

    RMQ(Range Minimum Query,区间最小值查询)与LCA(Lowest Common Ancestor,最近公共祖先)是两种常见的算法问题,广泛应用于数据结构、图论以及计算机科学竞赛中。 RMQ问题通常涉及到在一个给定的线性序列A中,...

    通过在C#中简化为RMQ来解决LCA

    总结来说,解决C#中的LCA问题可以通过将问题转化为RMQ,利用预先计算好的数据结构快速找出树中两个节点的最近公共祖先。这需要对RMQ算法有深入理解,并且能够熟练地运用C#编程语言和可能涉及到的.NET框架版本进行...

    Longest Common Ancestor

    LCA的算法证明基于深度优先搜索(DFS)的观察:在DFS过程中,u和v之间的最近公共祖先将是它们在Euler tour中第一次相遇的节点。Euler tour的大小是2n-1,其中n是树的节点数。 【算法实现】为了实现LCA,我们可以构建...

    Range Minimum Query and Lowest Common Ancestor[翻译]1

    接下来,文章转向了 Lowest Common Ancestor (LCA) 问题,即在树中找到两个节点的最近公共祖先。LCA 在图论和数据结构中有着重要应用,尤其是在生物信息学中用于分析物种的进化关系。 对于 LCA 的解决方案,文章也...

    代码 动态规划 特殊数据结构搜索、枚举

    1025 最近公共祖先(LCA) 也可转化为RMQ问题,见llj的书 1040 City Horizon 堆(线段树也可以,但速度慢) 模拟 1003 Iron String 积分模拟+二分逼近 1010 选队长 这题还可以用二叉树做 1012 整数游戏 1031 分礼物 二分...

    图论总结_ftiasch1

    - **Range Minimum Query(RMQ)**: LCA问题可以转化为RMQ问题,但通常不需要这种转化。 4. **二分图** - **Coloring property**: 一个图是二分图当且仅当它可以被两色染色,即图中不存在同色的相邻节点。奇环的...

    三峡大学算法设计与分析论文-倍增思想在算法运用与实现

    2.2 **最近公共祖先(LCA)**:在树形结构中,寻找两个节点的最近公共祖先。倍增算法可以高效地处理一系列节点对的LCA查询。 2.3 **区间极值问题(RMQ,Range Minimum/Maximum Query)**:在一个数组中,查询某个...

    吉大acm模板

    - **定义**: 预处理后能够快速查询最近公共祖先的算法。 - **应用场景**: 树结构操作等。 - **算法步骤**: - 使用预处理数组或倍增技巧。 ##### 3.10 带权值的并查集(Weighted Union Find) - **定义**: 并查集的...

    ACM必备内容(几乎全)!!!

    - **Lowest Common Ancestor (LCA)**:最近公共祖先问题。 - **RMQ to LCA**:将RMQ问题转化为LCA问题。 ##### 5.2 最长公共子序列(LCS) 最长公共子序列是在两个序列中找到最长的共同子序列。 ##### 5.3 最长上升...

    IOI国家集训队论文集1999-2019

    + [最近公共祖先](#最近公共祖先) + [划分问题](#划分问题) * [数论](#数论) + [欧几里得算法](#欧几里得算法) + [同余方程](#同余方程) * [搜索](#搜索) + [搜索](#搜索-1) + [启发式](#启发式) + [优化]...

    线段树例题(唐文斌).pdf

    - 将树转化为有根树,并采用 LCA 查询来找到两个节点的最低公共祖先。 - 通过预处理,可以将距离信息转换为树上路径的权值之和。 - 使用线段树来维护子树内的信息。 **解题思路:** - 将树转为有根树后,可以通过...

Global site tag (gtag.js) - Google Analytics