RMQ的两种实现方法

来源:互联网 发布:培训机构网站源码 编辑:程序博客网 时间:2024/06/05 15:43

    • 引言
    • 线段树实现RMQ
      • 主要思路
      • 代码实现
      • 时间复杂度
    • ST表实现RMQ
      • 主要思路
      • 代码实现
      • 时间复杂度

引言

RMQ算法(Range Minimum/Maximum Query) 是求区间极值的高效算法,依据所需实现的不同性能可以有多种写法,这里主要讲基于线段树和稀疏表(Sparse Table)的两种方法

线段树实现RMQ

主要思路

线段树是维护区间的一类高效数据结构,依据这个特性,我们可以用线段树实现RMQ算法,用线段树实现的RMQ算法不仅可以查询区间最小值,还可以更改某个节点的值

代码实现

#include<bits/stdc++.h>using namespace std;typedef long long int LL;const int MAXN=18;const int INF=1e9;int num[2*(1<<MAXN)];int n;void init(int base){    n=1;    while(n<base){        n<<=1;    }    for(int i=0;i<2*n-1;i++){        num[i]=INF;    }}void change_k(int k,int a){//将节点k的值赋为a     k+=n-1;    num[k]=a;    while(k){        k=(k-1)>>1;        num[k]=min(num[2*k+1],num[2*k+2]);    }}int get_min(int a,int b,int k,int l,int r){    if(a<=l&&b>=r) return num[k];    else if(a>=r||b<=l) return INF;    else {//      cout<<l<<" "<<r<<endl;        // 递归至左右子树求解         int lc=get_min(a,b,2*k+1,l,(l+r)/2);        int rc=get_min(a,b,2*k+2,(l+r)/2,r);        return min(lc,rc);    }}int main(){    scanf("%d",&n);    int cnt=n;    init(n);    for(int i=0;i<cnt;i++){        int tmp;        scanf("%d",&tmp);        change_k(i,tmp);    }    int a,b;    scanf("%d %d",&a,&b);    /*    int k,num;    scanf("%d %d",&k,&num);    change_k(k,num);    这里还可以实现更改     */    printf("%d\n",get_min(a,b,0,0,n)==INF?-1:get_min(a,b,0,0,n));}

时间复杂度

预处理时,操作的节点个数为n+n/2+n/4+……,约为2n个,所以复杂度为O(n),每一次查询与修改区间值的复杂度是O(log n).

ST表实现RMQ

主要思路

线段树的查询复杂度为O(log n),对于有多组询问的题还是太慢,有了线段树实现的铺垫,我们思考,是否有一种方法能预先处理出区间极值呢,答案是有的,就是ST表

代码实现

#include<bits/stdc++.h>using namespace std;const int MAXN=18;int dp[(1<<MAXN)][MAXN];int a[(1<<MAXN)];int n;void st_init(){    for(int i=0;i<n;i++){        dp[i][0]=a[i];    }    for(int j=1;(1<<j)<=n;j++){        for(int i=0;i+(1<<j)<=n;i++){            dp[i][j]=min(dp[i][j-1],dp[i+(1<<(j-1))][j-1]);        }    }}int get_min(int l,int r){    int k=(int)(log((double)r-l+1)/log(2.0));    return min(dp[l][k],dp[r-(1<<k)+1][k]);}int main(){    scanf("%d",&n);     for(int i=0;i<n;i++){        scanf("%d",&a[i]);    }    st_init();    int l,r;    scanf("%d %d",&l,&r);    printf("%d\n",get_min(l,r));}

时间复杂度

显而易见,预处理时的复杂度是O(n log n)的,而查询的复杂度为O(1),可以高效解决多组询问的题目,但基于ST表的写法无法对区间的值进行高效更新.

原创粉丝点击