现在的位置: 首页 > 综合 > 正文

LCA转RMQ

2013年10月21日 ⁄ 综合 ⁄ 共 1767字 ⁄ 字号 评论关闭

转自:http://blog.sina.com.cn/s/blog_5ceeb9ea0100kynz.html

LCA(a,b)转RMQ就是把从节点a到节点b的这条路径记下来(用dfs访问变成一维数组),然后求这条路径上面深度值最小的那个节点,这个节点的编号就是所求的LCA。至于为什么说深度最小的这个节点为所求是因为深度最小这个节点当然在这条路径的最上面,故为所求

转载+修改

一、最近公共祖先(Least Common Ancestors)
对于有根树T的两个结点u、v,最近公共祖先LCA(T,u,v)表示一个结点x,满足x是u、v的祖先且x的深度尽可能大。另一种理解方式是把T理解为一个无向无环图,而LCA(T,u,v)即u到v的最短路上深度最小的点。
这里给出一个LCA的例子:
例一
对于T=<V,E>
V={1,2,3,4,5}
E={(1,2),(1,3),(3,4),(3,5)}
则有:
LCA(T,5,2)=1
LCA(T,3,4)=3
LCA(T,4,5)=3

RMQ问题与LCA问题的关系紧密,可以相互转换,相应的求解算法也有异曲同工之妙。下面给出LCA问题向RMQ问题的转化方法。

对树进行深度优先遍历,每当“进入”或回溯到某个结点时,将这个结点的编号存入数组E最后一位。同时记录结点i在数组中第一次出现的位置(事实上就是进入结点i时记录的位置),记做R[i]。如果结点E[i]的深度记做D[i],易见,这时求LCA(T,u,v),就等价于求E[RMQ(D,R[u],R [v])],(R[u]<R[v]),其中RMQ(D,R[u],R [v])就是在D数组中求下标从R[u]到R[v]的最小值的下标。例如一,求解步骤如下:

数列E[i]为:1,2,1,3,4,3,5,3,1

R[i]为:1,2,4,5,7

D[i]为:0,1,0,1,2,1,2,1,0

于是有:

LCA(T,5,2) = E[RMQ(D,R[2],R[5])] = E[RMQ(D,2,7)] = E[3] = 1

LCA(T,3,4) = E[RMQ(D,R[3],R[4])] = E[RMQ(D,4,5)] = E[4] = 3

LCA(T,4,5) = E[RMQ(D,R[4],R[5])] = E[RMQ(D,5,7)] = E[6] = 3

易知,转化后得到的数列长度为树的结点数的两倍减一, 所以转化后的RMQ问题与LCA问题的规模同次。

 

 

 

再举一个例子帮助理解:

   (1)

    / \

(2)   (7)

/ \     \

(3) (4)   (8)

    /   \

(5)    (6)

一个nlogn 预处理,O(1)查询的算法.

Step 1:

        按先序遍历整棵树,记下两个信息:结点访问顺序和结点深度.

        如上图:

        结点访问顺序是: 1 2 3 2 4 5 4 6 4 2 1 7 8 7 1 //共2n-1个值

        结点对应深度是: 0 1 2 1 2 3 2 3 2 1 0 1 2 1 0

Step 2:

        如果查询结点3与结点6的公共祖先,则考虑在访问顺序中

        3第一次出现,到6第一次出现的子序列: 3 2 4 5 4 6.

        这显然是由结点3到结点6的一条路径.

        在这条路径中,深度最小的就是最近公共祖先(LCA). 即

        结点2是3和6的LCA.

Step 3:

        于是问题转化为, 给定一个数组R,及两个数字i,j,如何找出

        数组R中从i位置到j位置的最小值..

        如上例,就是R[]={0,1,2,1,2,3,2,3,2,1,0,1,2,1,0}.

        i=2;j=7;

        接下来就是经典的RMQ问题.

 

 

总结:

RMQ是给定一列数,动态询问[i,j]区间内的最小(或最大值)。

LCA是给定一棵树,动态询问u和v的最近公共祖先。

解决这两种问题都有个很重要的倍增思想(这个思想在后缀数组方面亦有所应用)。

关键需要记住的是

在LCA预处理的时候

p[i,j] 表示i的2^j 倍祖先

那么就有一个递推式子 p[i,j]=p[p[i,j-1],j-1]

RMQ和LCA可以相互转化。。   所以只要记住一种就行了。。

RMQ转LCA的时候是生成一棵类似于堆的递归树;LCA转RMQ的时候用到的是深度优先遍历。

主要掌握的不在于算法,而是在于倍增思想

抱歉!评论已关闭.