前言
这篇文章,是一篇总结RMQ算法的一篇文章,若有疑问或建议,请于下方留言,谢谢!
RMQ是什么?
RMQ(Range Minimum/Maximum Query),即区间最值查询。
是指这样一个问题:对于长度为n的数列A,回答若干询问RMQ(A,i,j)(i,j<=n),返回数列A中下标在i,j之间的最小/大值。
这两个问题是在实际应用中经常遇到的问题,而RMQ算法是解决这两种问题的比较高效的算法。
当然,该问题也可以用线段树解决,算法复杂度为:O(N)~O(logN),而线段树代码远远比我们今天讨论的RMQ要长要难理解的多,这里我们暂不介绍。
我们遇到这种区间最值查询问题的时候,通常会想到两种想法
每次查询时暴力扫描区间,时间复杂度单次查询O(n),n次查询便是O(n^2),超过1e4基本上就TLE了
开一个二维数组(空间复杂度O(n*n)),预处理答案在数组中,预处理复杂度O(n^2),单次查询O(1)。
显然这些算法都是渣渣,慢的要死,那么有什么搞基高级的方法来解决这一类问题呢?
答案是倍增思想。(大概是这样的)
RMQ算法描述
我们以最大值来诠释RMQ算法
- 令A[i]存储我们要求的数列,F[i][j]表示从第i个位置开始,往后的2^j-1个数字中,最大的数字是多少。
- 预处理F数组,对j从大到小枚举。每次处理F[i][j]的时候,F[i][0~j-1]都已经处理好了,那么F[i][j]=max(F[i][j-1],F[i+2^(j-1)][j-1]),因为j最大到log2n,所以预处理的操作的时间复杂度为O(nlog2(n))。
- 有了F数组啦,现在我们查询的话就很简单了。比如说我们查询区间(i,j),那么我们令k =⌊log2(i)+j-1⌋,那么我们查询的结果就是max(F[i][j],F[j-(2^k)+1][k]),可以明显看到时间复杂度为O(1)的,而且保证能够覆盖到整个区间。
代码描述如下
预处理操作
void init_rmq(int m) {
for(