高级数据结构(2)、ST表

标签: ST表 RMQ
10人阅读 评论(0) 收藏 举报
分类:

时隔多年,我又来更新了233

昨天听雨神讲了一发差分/树上差分的问题,并且提到了LCA的求法,于是今天先决定更一发RMQ问题,然后过两天再更LCA的问题QAQ

RMQ(Range Minimum/Maximum Query),即区间最值查询问题。该问题描述的是在一个数组上,如何快速地求出给定区间[ L , R ]的最值的问题。例如对于给定数组[ 3 , 2 , 4 , 5 , 6 , 8 , 1 , 2 , 9 , 7],查询区间      [ 1 , 3 ]的最大值就是第三个数4,最小值就是第二个数2.

对于该问题,我们最先想到的是O(n) 的算法,即直接遍历一遍所给区间[ L , R ],即可求得最大值与最小值。但是如果这样的查询很频繁,造成的时间复杂度会很大,是我们难以接受的。于是我们需要想一个在线的算法,使得我们能够在线查询所给区间的最值。

我们先分析一手,为什么我们连续查询会造成比较大的复杂度。例如现在我们要查询[ 1 , 3 ]这个区间的最小值,可以求得是2;然后我们查询[ 2 , 5 ]和[ 1 , 4 ]的最小值,可以看到答案都是2,但是我们发现我们在这个过程中做了很多重复的工作。实际上,我们查询[ 1 , 4 ]的时候,只要查询min( [ 1 , 3 ] 和 [ 4 , 5 ] )就可以了,[ 1 , 3 ]的过程我们重复查询了一遍,这样就造成了额外的开销。那我们能不能先预处理整个数组,得到我们所需的全部信息,然后实现O(1)的查询呢?

这时候我们可以联想到动态规划的思想。动态规划是为了解决大量重复子问题而提出的,我们可以借助这个思想,来对我们的数组进行预处理,即区间dp。既然要用到动态规划,我们就要思考一下状态是什么。

在这里,我们使用了一个倍增的思想。即我们每次在区间增长的时候,并不是给区间+1,而是给区间*2。至于为什么要倍增,是因为倍增的话可以让每一次操作的复杂度由n降低为logn。所以我们就可以定义dp[i][j]为从第i个点开始,长度为2^j的区间的最大/最小值。显然,dp[i][0]代表的就是数组中每个元素对应的位置的最值,即该元素本身。

dp的状态和边界都找到了,这时候我们就要考虑一下状态的转移了。在这儿,我们找到的每一段区间必然都是偶数长度的(因为是2^j),那我们就可以把它拆成2个相同长度的子区间,每个子区间的长度为2^(j-1)。原区间的最值就等于这两个子区间的最值的最值。以最小值为例,dp[i][j]=min(dp[i][j-1],dp[i+(1<<j-1)][j-1])

至此,我们已经预处理好了原数组,接下来就是查询了。但是有个小小的问题,即我们在预处理数组的时候,存储最值的区间长度都是2的j次方,而待查询的区间不一定正好是2^j,所以在查询的时候,我们依旧要对于待查区间进行预处理,方法如下。首先,我们将待查区间分成长度几乎相等的两个子区间,例如我们查询[ 4 , 12 ]这个区间,就分成[ 4 , 8 ]和[ 9 , 12 ]这两个区间,长度分别为5和4,然后扩大这两个区间,使得它们恰好为2的幂。这儿4就可以不动了,然后5就可以扩大成8。这儿需要注意的是,扩大后的两个区间是有可能重叠的,但是这并不会影响我们答案的正确性。这样我们就能O(1)地计算出这两个区间的最值的最值了。下面贴代码:

#include<bits/stdc++.h>
using namespace std;

const int maxn=1e5+7;
int stmax[maxn][30];
int stmin[maxn][30];
int a[maxn];

void rmq_st(int n){//预处理过程
    for(int i=1;i<=n;i++)
        stmax[i][0]=stmin[i][0]=a[i];//初始化
    int m=(int)(double(log(n))/log(2.0));//换底公式
    for(int j=1;j<=m;j++)//这儿切记,这两个循环顺序是不能颠倒的!
    for(int i=1;i+(1<<j)-1<=n;i++){
        stmax[i][j]=max(stmax[i][j-1],stmax[i+(1<<j-1)][j-1]);
        stmin[i][j]=min(stmin[i][j-1],stmin[i+(1<<j-1)][j-1]);
    }
}

void rmq_query(int l,int r){
    int k=(int)((double)log(r-l+1)/log(2.0));
    cout<<"Max is : "<<max(stmax[l][k],stmax[r-(1<<k)+1][k])<<endl;
    cout<<"Min is : "<<min(stmin[l][k],stmin[r-(1<<k)+1][k])<<endl;
}

int main(){
    int n;
    cin>>n;
    for(int i=1;i<=n;i++)
        cin>>a[i];
    rmq_st(n);
    int l,r;
    while(cin>>l>>r){
        rmq_query(l,r);
    }
    return 0;
}


查看评论

数据结构基础系列(2):线性表

-
  • 1970年01月01日 08:00

《 常见算法与数据结构》符号表ST(1)——基本介绍

符号表(Symbol Table) 这次该介绍符号表(Symbol Table)了,这可是个很有用的数据结构 本系列文章主要介绍常用的算法和数据结构的知识,记录的是《Algorithms I/I...
  • hk2291976
  • hk2291976
  • 2016-05-14 15:10:19
  • 4183

查找表【严蔚敏】

查找表:同一类型元素或记录构成的集合。 对查找表的常用操作: 1)查找某个元素是否在表中; 2)检查某个元素的各个属性; 3)插入一个新数据元素; 4)删除某个数据元素。 静态查找表:只有...
  • MooMLu
  • MooMLu
  • 2017-04-26 19:35:21
  • 158

数据结构:静态查找表(顺序表)

#include #include #define OVERFLOW -2 #define FALSE  0 #define TRUE 1 typed...
  • s634772208
  • s634772208
  • 2015-05-08 22:57:49
  • 787

从零开始_学_数据结构(四)——查找算法、索引、二叉排序树

查找算法   基本概念: (1)关键字:假如有结构 struct Node //一个结点,存储数据和指针 { DATA data; //数据属性,用于存储数据 int key; ...
  • qq20004604
  • qq20004604
  • 2016-03-25 16:47:09
  • 1594

ST表

风满山楼,执吾之剑,破万重天!
  • WhiStLenA
  • WhiStLenA
  • 2016-08-12 14:38:22
  • 7450

RMQ--ST表算法理解

RMQ(Range Minimum/Maximum Query),即区间最值查询,是指这样一个问题:对于长度为n的数列A,回答若干询问RMQ(A,i,j)(i,j ST算法(Sparse Tabl...
  • qq1169091731
  • qq1169091731
  • 2016-07-21 11:59:59
  • 1690

ST表算法详解

ST表算法详解(算是吧)ST表就是一个用来解决rmq(区间最值)问题的算法。 ST表不支持在线修改。 预处理时间复杂度O(nlogn),查询时间O(1)。 ST表算法详解(求最小值): 用mn...
  • Hanks_o
  • Hanks_o
  • 2017-08-25 08:21:55
  • 1032

ST算法详解+例题 O(1)查询区间最大最小值

RMQ问题, ST算法。
  • u013508213
  • u013508213
  • 2015-08-08 21:04:09
  • 660

数据结构 - 表插入排序 详解 及 代码(C++)

表插入排序 详解 及 代码(C++) 本文地址: http://blog.csdn.net/caroline_wendy/article/details/24323125 表插入排序(List...
  • u012515223
  • u012515223
  • 2014-04-22 19:12:32
  • 2239
    个人资料
    持之以恒
    等级:
    访问量: 375
    积分: 122
    排名: 119万+
    文章分类
    文章存档