ST算法采用的是倍增的思想,先用O(nlogn)的时间构造一个二维表之后,可以在O(1)的时间查询[l,r]区间的最值
1.ST创建
若F[i,j]表示[i,i+2^j-1]区间的最值,区间长度为2^j,则i和j的取值范围是多少呢?
若数组的长度为n,最大区间长度2^(k)<=n<2^(k+1),则k=[log2(n)],比如n=8时k=3.
在程序中,k=log2(n),也可用通用表达方式k=log(n)/log(2),log()表示以e为底的自然对数
算法代码:
void ST_create()//创建ST
{
for(int i=1;i<=n;i++)//初始化
{
F[i][0]=a[i];//表示[i,i]区间的最值,区间长度为2^0
}
int k=log2(n);
for(int j=1;j<=k;j++)
{
for(int i=1;i<=n-(1<<j)+1;i++)
{
F[i][j]=max(F[i][j-1],F[i+(1<<(j-1))][j-1]);
}
}
}
例如有10个元素a[1..10]={5,3,7,2,12,1,6,4,8,15},其查询最值的ST如下图所示。
F[i,j]表示[i,i+2^j-1]区间的最值,区间长队为2^j
F[1,0]表示[1,1+2^0-1]区间,即[1,1]的最值为5,第0列为数组自身
F[1,1]表示[1,1+2^1-1]区间,即[1,2]的最值为5
F[2,3]表示[2,2+2^3-1]区间,即[2,9]的最值为12
F[6,2]表示[6,6+2^2-1]区间,即[6,9]的最值为8
F[][] j 0 1 2 3
i 1 5 5 7 12
2 3 7 12 12
3 7 7 12 15
4 2 12 12
5 12 12 12
6 1 6 8
7 6 6 15
8 4 8
9 8 15
10 15
2.ST查询
若查询[l,r]区间的最值,则首先计算k值,和前面的计算方法相同,区间长度为r-l+1,
2^k<=r-l+1<2^(k+1),因此k=log2(r-l+1).
若查询区间的长度大于或等于2^k且小于2^(k+1),则根据倍增的思想,可以将查询区分为两个查询区间
取两个区间的最值即可。两个区间分别为从l向后的2^k个数及从r向前的2^k个数,这两个区间可能有重叠
但对求最值没有影响。
int ST_query(int l,int r)
{
int k=log2(r-l+1);
return max(F[1][k],F[r-(1<<k)+1][k]);//取两个区间的最值
}
3.算法分析
创建ST时,初始化需要O(n)时间,两个for循环需要O(nlogn)时间,总时间复杂度为O(n),
区间查询实际上是查表的过程,复杂度为O(1)。一次建表多次使用,这种查表法就是动态规划