RMQ(range minimum/maximum query)即查询区间最大最小值。
对于求区间最大最小值,我们自然而然就想到了一个O(n)时间复杂度的算法,但是如果询问有很多呢?这样必然超时。当然我们可以用线段树来解,使得每一次查询的时间降到log(n),但是对于RMQ算法,只要我们做了些预处理,之后的查询我们仅需要O(1)的时间。Sparse_Table算法是解决RMQ问题的一类较好的算法,属于一种在线算法,至于什么叫在线什么叫离线,先简单介绍一下。
在线算法:在计算机科学中,一个在线算法是指它可以以序列化的方式一个个的处理输入,也就是说在开始时并不需要已经知道所有的输入。
离线算法:在开始时就需要知道问题的所有输入数据,而且在解决一个问题后就要立即输出结果。例如,选择排序在排序前就需要知道所有待排序元素,然而插入排序就不必了。
简单的概括一下 在线算法就是说程序先把预处理工作做好,对于之后的查询,可以很快给你答复。离线算法就是你先把需求告诉程序,他一次性给你解决。
好了,下面来讲解Sparse_Table算法
1.求最值数组
Sparse_Table算法的预处理就是一个动态规划的思想。
设数组maxn[i][j] 表示给定的数组从下标i开始,长度为2^j的区间最大值(最小值一样)也就是arr[i]----arr[i+2^j-1]这个区间的最大值。
于是我们可以写出这样一个动态转移方程maxn[i][j] = max(maxn[ i ][ j-1 ],maxn[ i+2^(j-1) ][ j-1 ]) 看懂了么?
其实就是把区间【i ,i+2^j -1】分成两段,一段是【i,i+2^(j-1)-1】 和【i+2^(j-1),i+2^j】(一直记住二维数组后面一维表示的是区间的长度2^j)
那么对于maxn[i][j]当j等于0,也就是区间长度为1的最大值显然就有maxn[i][0] = arr[i];
到此我们就可以写出Sparse_Table的预处理部分了
void getbestarr(int n)//n为给定的数组的长度 { int tem = (int)floor(log2((double)n));//因为区间的最长长度是2^tem==n嘛 for(int i=1;i<=n;i++) minn[i][0]= maxn[i][0] = arr[i]; for(int j=1;j<=tem;j++) //下标从1开始 for(int i=1;i+(1<<j)-1<=n;i++) { maxn[i][j] = max(maxn[i][j-1],maxn[i+(1<<(j-1))][j-1]); //最大值 minn[i][j] = min(minn[i][j-1],minn[i+(1<<(j-1))][j-1]); //最小值 } }
我们看看这个动态规划方程是怎么求解这个maxn,minn数组的
要求区间长度为2的肯定要先求出区间长度为1的嘛 比如区间[1,2]只要在区间[1,1] [2,2]中取最值嘛 长度为4的肯定要先算出长度为2的嘛 比如求区间[6,9]的最值只要在区间[6,7] [8,9]中取最值嘛。。。。。。
所以最外层的循环就肯定是区间的长度2^j次方咯 里面的循环应该都看得懂吧。
2.查询
这个最值区间的数组求出来了,下面就是查询了
比如要查询区间[a,b]的最值 怎么求呢?
注意到我们的最值数组存的都是区间长度为2^k(k=0,1,2,3.....)次方的最值
所以对于区间[a,b] 我们肯定要划分为两个区间长度是2^x 2^y的区间才可以直接利用我们得到的最值数组来求最值嘛
这里有两个未知数不好求,我们可以直接取k,对于k满足a+2^k-1=b k=log2(b-a+1) (注意这里不是a+2^k=b 还是那句话,始终记得2^k是区间的元素的个数) 那么区间a,b的最大值不就是max(maxn[a][k],maxn[b-2^k+1][k])比如对于区间长度为4的[3,6]求出k==2 于是最大值就是区间max(【3,6】,【3,6】)当然我们不能能保证log2(a-b+1)就一定能得到一个整数,但是这不要紧,比如对于区间长度为5的【3,7】我们对log2(7-3+1)取整得到2,于是最大值就在
max(【3,6】,【4,7】),max函数里面前面那个maxn[a][k]就保证了我们的求最值的区间以a开始,后面那个maxn[b-2^k+1][k]就保证了我们必然能够以b为尾
int query(int a,int b,bool getwhat)//getwhat表示你是想取最大还是最小 { int k = log2(b-a+1); if(getwhat) return max(maxn[a][k],maxn[b-(1<<k)+1][k]); else return min(minn[a][k],minn[b-(1<<k)+1][k]); }