滑动最小值(双端队列)

给定一个长度为n的数列a(0),a(1),...,a(n-1)和一个整数k。求数列b(i)=min{a(i),a(i+1),...,a(i+k-1)}(i=0,1,2,3,...,n-k);

限制条件:1<=k<=n<=10^6 0<=ai<=10^9

思路:类似用一个长度为k的窗在整数数列上滑动,求窗里面所包含的数的最小值;
可以使用RMQ在o(nlogn)复杂度内解决,本次介绍双端队列;
          双端队列(即单调队列),可以在头部和尾部插入和删除元素的数据结构;
          构造单调递增队列,初始双端队列为空,在加入i时,当队列的末尾的值j满足a[j]>=a[i],则不断取出(构造一个递增队列),直到队列为空或者a[j]<a[i]之后再在末尾加入i。则双端队列头部即为最小值。
时间复杂度:由于双端队列的加入和删除都进行了o(n)次,故整个算法的复杂度为o(n);

代码:

//输入
int n,k;
int a[maxn];

int b[maxn];
int deq[maxn];//双端队列

void solve(){
    int s=0,t=0;//双端队列的头部和尾部

    for(int i=0;i<n;i++)//在双端队列的末尾加入i
    { 
         while(s<t&&a[deq[t]]>=a[i])   t--;//加入i时,判断双端队列末尾的值是否满足>=a[i]
         deq[++t]=i;
         if(i-k+1>=0){
             b[i-k+1]=a[deq[s]];//等于双端队列头部
             if(deq[s]==i-k+1)//从双端队列的头部删除元素
                    s++;
          }
      }
      for(int i=0;i<=n-k;i++)
           printf("%d%c",b[i],i==n-k?
: );
}


经验分享 程序员 微信小程序 职场和发展