一、RMQ问题 Range Maximum/Minimum Query 区间最值查询
- 问题描述:给定一个数组A,回答询问RMQ(a, i, j),即a[i...j]之间的最值的下标。
- 最简单的算法是O(n)的,但是对于查询次数很多(如100万次),O(n)的算法效率不够。解决这个的问题的一种算法是线段树算法(在线段树中保存线段的最值),复杂度为O(logn)。不过,最好的算法是Sparse Table(ST)算法,它可以在O(nlogn)的预处理以后,实现O(1)的查询。
- ST算法(以查询最小值为例)
- 第一步:预处理
预处理使用DP(动态规划)的思想。f(i, j)表示[i, i+2^j - 1]区间中的最小值。例如,f(0, 0)表示[0,0]之间的最小值,就是a[0], f(0, 2)表示[0, 3]之间的最小值, f(2, 4)表示[2, 17]之间的最小值。采用自底而上的方法构造数组f(i, j),递推关系式为:
f(i, 0) = a[i]
f(i, j) = min( f(i,j - 1), f(i+2^(j-1), j-1))
- 第二步:查询
假设要查询从m到n这一段的最小值,那么我们先求出一个最大的k,使得k满足2^k <= (n - m + 1)。于是我们就可以把[m, n]分成两个长度为2^k的区间: [m,m+2^k-1], [n-2^k+1, n](有部分重叠);而我们之前已经求出了f(m, k)为[m, m+2^k-1]的最小值, f(n-2^k+1, k)为[n-2^k+1,n]的最小值我们只要返回其中更小的那个,就是我们想要的答案,这个算法的时间复杂度是O(1)。
例如, RMQ(0, 11) = min(f(0, 3), f(4, 3))
二、LCA问题 Lowest Common Ancestor 最低公共祖先
- 问题描述:对于一棵有根树T的任意两个节点u、v,求出LCA(T, u, v),即离根最远的节点x,使得x同时是u和v的祖先。
在线算法:用比较长的时间做预处理,但是等信息充足以后每次回答询问只需要用比较少的时间
离线算法:先把所有的询问读入,然后一起回答所有的询问
- 算法一:Tarjan算法(离线算法)
一次读入所有LCA询问,然后回答。Tarjan算法是通过并查集和DFS结合实现的,其伪代码为:
LCA(u)
{
Make-Set(u)
ancestor[Find(u)]=u
对于u的每一个孩子v
{
LCA(v)
Union(u)
ancestor[Find(u)]=u
}
checked[u]=true
对于每个(u,v)属于P /* P存储所有的LCA询问 */
{
if checked[v]=true
then {
回答u和v的最近公共祖先为 ancestor[Find(v)]
}
}
}
其中,makest就是建立一个集合,makeset(u)就是建立一个只含U的集合。
并查集是一种处理元素之间等价关系的数据结构,一开始我们假设每个元素都是分别属于一个独立的集合,主要有两种操作:
合并两个不相交集合(Union)
查询元素所属的集合,返回根节点,根节点代表这个集合(Find)
- 算法二:转化为RMQ问题
1) DFS:从树T的根开始,进行深度优先遍历,并记录下每次到达的顶点。第一个的结点是root(T),每经过一条边都记录它的端点。由于每条边恰好经过2次,因此一共记录了2n-1个结点,用E[1, ... , 2n-1]来表示。
2) 计算R:用R[i]表示E数组中第一个值为i的元素下标,即如果R[u] < R[v]时,DFS访问的顺序是E[R[u], R[u]+1, ..., R[v]]。虽然其中包含u的后代,但深度最小的还是u与v的公共祖先。
3) RMQ:当R[u]≥ R[v]时,LCA[T, u, v] = RMQ(E, R[v], R[u]);否则LCA[T, u, v] = RMQ(E, R[u], R[v]),计算RMQ。
由于RMQ使用的ST算法是在线算法,所以这个算法也是在线算法。
如图,DFS遍历得到的数组E[1,…,9]为:
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
1 | 2 | 4 | 2 | 5 | 2 | 1 | 3 | 1 |
数组R[1,…,5]为:
1 | 2 | 3 | 4 | 5 |
1 | 2 | 8 | 3 | 5 |
例:LCA[T, 3, 4] = RMQ(E, R[4], R[3]) = RMQ[E, 3, 8] = E[7] = 1
LCA[T, 4, 5] = RMQ(E, R[4], R[5]) = RMQ[E, 3, 5] = E[4] = 2
另见文章http://www.topcoder.com/tc?module=Static&d1=tutorials&d2=lowestCommonAncestor