介绍
ST表:一种用于高效处理区间查询的数据结构。它可以在O(1)的时间复杂度内回答某一区间的最值查询(最小值、最大值等)。ST表使用动态规划的思想,通过预处理的方式来快速计算出各个区间的最值。
st算法的主要思想就是将所求的区间化为两个小区间,这两个区间的长度正好是2的k次幂,总长度正好覆盖[l,r],得到的结果就是所求答案。
st[ i ][ j ]表示从i开始,当度为2^j 的区间长度(包含2^j个元素)
那么我们可以对一个包含2^j的数字的区间[i,i+2^j-1](注意要减1,可以举例子理解)进行维护。
建表
定义st[i][j]为以i为起点,包含2^j个数的区间。由引入部分的图解,我们可以写出递推式
st[i][j] = max (st[i][j - 1] , st[i + (1 << j - 1)][j - 1]);
//i << j - 1 就是 2 ^ (j - 1),代码一定要这么写
因为递推需要知道子区间的最值,最小子区间就是单个数字,也就是st[i][0],所以需要先对st[i][0]赋初始值。然后从j=1开始,枚举所有包含2^j个数字的区间。代码如下
for (int i = 1; i <= n; i++) cin >> stmax[i][0];
for (int j = 1; (1 << j) <= n; j++)//总包含数字不超过n
{
for (int i = 1; i + (1 << j) - 1 <= n; i++)//确保是子区间,不越界
{
stmin[i][j] = min(stmin[i][j - 1], stmin[i + (1 << j - 1)][j - 1]);
stmax[i][j] = max(stmax[i][j - 1], stmax[i + (1 << j - 1)][j - 1]);
}
}
查表
定义两个变量l,r为查找区间的左右端点。要查区间的最值,只需分别查如图两段包含2^x个数的区间的最值,再进行比较,就得到所查区间的最值。为此,我们必须保证,两端区间必须包含了所有所查区间的数。因此我们需要得出最大的x。2^x<2^j即2^x<r-l+1,得出x<log2(r-l+1),x最大就向下取整。两端区间的端点分别是l和 r-(1<<x)+1
代码如下
int quary(int l, int r)
{
int j = (int)log2(r - l + 1);
return max(dp[l][j], dp[r - (1 << j) + 1][j]);
}