【转载】最近公共祖先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,实例代码:
Cpp代码   收藏代码
  1. #include<iostream>  
  2. #include<vector>  
  3. #include<cmath>  
  4. #include<algorithm>  
  5. using namespace std;  
  6.   
  7. const int maxn=20010;  
  8.   
  9. struct node //可以添加额外的信息  
  10. {  
  11.     int v;//孩子结点  
  12. };  
  13.   
  14. //注意vector在树问题中的使用  
  15. vector<node> tree[maxn];  
  16.   
  17. int dfsnum[maxn]; //记录遍历的节点  
  18. int depth[maxn]; //记录节点对应的深度  
  19. int first[maxn]; //记录结点第一次访问到时的下标  
  20. int top; //记录总的步伐数  
  21.   
  22. void dfs(int m,int f,int dep) //当前节点编号,父节点编号,深度  
  23. {  
  24.     dfsnum[top]=m;  
  25.     depth[top]=dep;  
  26.     first[m]=top;  
  27.     top++;  
  28.     for(unsigned i=0;i<tree[m].size();i++)  
  29.     {  
  30.         if(tree[m][i].v==f)  
  31.             continue;  
  32.         dfs(tree[m][i].v,m,dep+1);  
  33.         dfsnum[top]=m; //注:每条边回溯一次,所以top的值=n+n-1  
  34.         depth[top]=dep;  
  35.         top++;  
  36.     }  
  37. }  
  38.   
  39. int dp[maxn][18];  
  40. void makeRmqIndex(int n,int b[]) //返回最小值对应的下标  
  41. {  
  42.     int i,j;  
  43.     for(i=0;i<n;i++)  
  44.         dp[i][0]=i;  
  45.     for(j=1;(1<<j)<=n;j++)  
  46.         for(i=0;i+(1<<j)-1<n;i++)  
  47.             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];  
  48. }  
  49. int rmqIndex(int s,int v,int b[])  
  50. {  
  51.     int k=(int)(log((v-s+1)*1.0)/log(2.0));  
  52.     return b[dp[s][k]]<b[dp[v-(1<<k)+1][k]]? dp[s][k]:dp[v-(1<<k)+1][k];  
  53. }  
  54.   
  55. int lca(int x,int y)  
  56. {  
  57.     return dfsnum[rmqIndex(first[x],first[y],depth)];  
  58. }  
  59.   
  60. int main()  
  61. {  
  62.     int n=5;//顶点数  
  63.     top=0;  
  64.     //分别存放每条边的端点  
  65.     int x[]={1,1,3,3};  
  66.     int y[]={2,3,4,5};  
  67.     node temp;  
  68.     for(int i=0;i<n-1;i++) //n-1条边  
  69.     {  
  70.         temp.v=y[i];  
  71.         tree[x[i]].push_back(temp);  
  72.         temp.v=x[i];  
  73.         tree[y[i]].push_back(temp);  
  74.     }  
  75.     dfs(1,-1,0); //根节点为1  
  76.     cout<<"总数:"<<top<<endl;  
  77.     makeRmqIndex(top,depth);  
  78.     cout<<"dfsnum:";  
  79.     for(int i=0;i<top;i++)  
  80.     {  
  81.         cout<<dfsnum[i]<<" ";  
  82.     }  
  83.     cout<<endl;  
  84.     cout<<"depth:";  
  85.     for(int i=0;i<top;i++)  
  86.     {  
  87.         cout<<depth[i]<<" ";  
  88.     }  
  89.     cout<<endl;  
  90.     cout<<"first:";  
  91.     for(int i=1;i<=n;i++)  
  92.     {  
  93.         cout<<first[i]<<" ";  
  94.     }  
  95.     cout<<endl;  
  96.     cout<<"lca(4,5):"<<lca(4,5)<<endl;  
  97.     cout<<"lca(2,4):"<<lca(2,4)<<endl;  
  98.     cout<<"lca(1,4):"<<lca(1,4)<<endl;  
  99.     return 0;  


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值