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表的写法无法对区间的值进行高效更新.

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值