RMQ问题
RMQ问题就是Range max/min Query
。
A数列为:3 2 4 5 6 8 1 2 9 7
对于数列A,回答查询RMQ(A,i,j)的最大/小值:返回在数组A中下标在i和j之间的最小/最大值。
最容易想到的问题是 O(n) 的查询,当查询数量过多的时候,时间花费可能很多。ST算法可以在 O(nlogn) 的时间内预处理完成后, O(1) 的时间内查询。
在线算法(ST算法)
在O(nlogn)时间内进行预处理,然后在O(1)时间内回答每个查询。
A数列为:3 2 4 5 6 8 1 2 9 7
F[1,0]
表示第1个数起,长度为2^0=1的最大值,其实就是3这个数。
同理
F[1,1] = max(3,2) = 3
F[1,2] = max(3,2,4,5) = 5
F[1,3] = max(3,2,4,5,6,8,1,2) = 8
并且我们可以容易的看出F[i,0]
就等于A[i]
。(DP的初始值)
F[i, j]=max(F[i,j-1], F[i + 2^(j-1),j-1])
比如F[1,3] = max(F(1,2),F(1+2^2,2))
void RMQ(int num) //预处理->O(nlogn),得到maxsum[i][j]以及minsum[i][j]的值
//i和j的范围是i + (1 << j) < num
{
for(int j = 1; j < 20; ++j)
for(int i = 1; i <= num; ++i)
if(i + (1 << j) < num)
{
maxsum[i][j] = max(maxsum[i][j - 1], maxsum[i + (1 << (j - 1))][j - 1]);
minsum[i][j] = min(minsum[i][j - 1], minsum[i + (1 << (j - 1))][j - 1]);
}
}
在查询的时候
比如要求区间[2,8]的最大值F[2,8], k=log2(8−2+1)=2 ,即求:
F[2,8] = max(maxsum[2][2],maxsum[8-2^2+1][2]) = max(maxsum[2,2],maxsum[5,2]);
线段树
暂时空着。
LCA问题
在一棵没有环的树上,每个节点肯定有其父亲节点和祖先节点,而最近公共祖先,就是两个节点在这棵树上深度最大的公共的祖先节点。
4和5的最近公共祖先是2
5和3的最近公共祖先是1
2和1的最近公共祖先是1
离线算法Tarjan
下面详细介绍一下Tarjan算法的基本思路:
- 任选一个点为根节点,从根节点开始。
- 遍历该点u所有子节点v,并标记这些子节点v已被访问过。
- 若是v还有子节点,返回2,否则下一步。
- 合并v到u上。
- 寻找与当前点u有询问关系的点v。
- 若是v已经被访问过了,则可以确认u和v的最近公共祖先为v被合并到的父亲节点a。
Tarjan(u)// marge和find为 并查集 合并函数 和 查找函数
{
for each(u,v) //访问所有u子节点v
{
Tarjan(v); //继续往下遍历
marge(u,v); //合并v到u上
标记v被访问过;
}
for each(u,e) //访问所有和u有询问关系的e
{
如果e被访问过;
u,e的最近公共祖先为find(e);
}
}