您的位置:首页 > 其它

【转载】最近公共祖先LCA:RMQ转化

2012-03-22 10:32 267 查看

最近公共祖先LCA:RMQ转化

博客分类:
算法J#F#

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,实例代码:

Cpp代码  


#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;  
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  tree vector 算法 ini