week5 D题滑动窗口 单调队列

题目:
ZJM 有一个长度为 n 的数列和一个大小为 k 的窗口, 窗口可以在数列上来回移动. 现在 ZJM 想知道在窗口从左往右滑的时候,每次窗口内数的最大值和最小值分别是多少.
例如:数列是[1 3 -1 -3 5 3 6 7], 其中k 等于 3.
在这里插入图片描述
输入:
输入有两行。第一行两个整数n和k分别表示数列的长度和滑动窗口的大小,1<=k<=n<=1000000。第二行有n个整数表示ZJM的数列。
输出:
输出有两行。第一行输出滑动窗口在从左到右的每个位置时,滑动窗口中的最小值。第二行是最大值。
样例输入:
8 3
1 3 -1 -3 5 3 6 7
样例输出:
-1 -3 -3 -3 3 3
3 3 5 5 6 7

本题可以用两个单调队列解决,一个是单调递增的队列,一个是单调递减的队列。
比如求最大值,根据窗口大小k,先循环将k个元素压入单调递减的队列,如果队列为空,则元素压入,如果队列末尾元素小于要压入的队列,则将末尾元素弹出,直到末尾元素大于当前要压入的元素,则队首元素为当前窗口内最大的元素。此外,还要考虑在窗口移动过程中是否有元素即使在压入新元素的过程中没有被弹出,但是已经陈旧了,即在窗口的左外部,这种元素也要弹出。同时,因为越靠近队首的元素序号越靠前,可以依据队首的序号判断元素是否过于陈旧,只需要判断一次,因为窗口一次移动一位。
此外,对于本题,要使用scanf和printf进行输入输出,用cin和cout会超时。

以下是完整代码:

#include<iostream>
using namespace std;
struct node{
 int rank;
 int ele;
};
class Maxqueue{
 private:
  int first,last;
  node *q;
 public:
  Maxqueue()
  {
   first=0;
   last=0;
   q=new node[2000000];
  }
  int back()
  {
   return q[last-1].ele;
  }
  int front()
  {
   return q[first].ele;
  }
  bool empty()
  {
   return first==last;
  }
  void pop_back()
  {
   last--;
  }
  void push_back(node x)
  {
   if(!empty())//在队列不空的情况下才可以用back()函数 
   {
    while(!empty()&&back()<x.ele)//将队尾所有小于x的元素弹出 
    {
     pop_back();
    }
   }
   
   q[last].ele=x.ele;
   q[last++].rank=x.rank;
  }
  int front_rank()
  {
   return q[first].rank;
  }
  void pop_front()
  {
   first++;
  }
};
class Minqueue{
 private:
  int first,last;
  node *q;
 public:
  Minqueue()
  {
   first=0;
   last=0;
   q=new node[2000000];
  }
  int back()
  {
   return q[last-1].ele;
  }
  int front()
  {
   return q[first].ele;
  }
  bool empty()
  {
   return first==last;
  }
  void pop_back()
  {
   last--;
  }
  void push_back(node x)
  {
   if(!empty())
   {
    while(!empty()&&back()>x.ele)
    {
     pop_back();
    }
   }
   
   q[last].ele=x.ele;
   q[last++].rank=x.rank;
  }
  int front_rank()
  {
   return q[first].rank;
  }
  void pop_front()
  {
   first++;
  }
};

node a[2000000];
int main()
{
 Maxqueue maq;
 Minqueue miq;
 int n,k;
 cin>>n>>k;
  for(int i=0;i<n;i++)
 {
  scanf("%d",&a[i].ele);
  a[i].rank=i;//保存元素的序号 
 }
 for(int i=0;i<k;i++)
 {
  maq.push_back(a[i]);
  miq.push_back(a[i]); 
 }
 int l=0,r=k-1;
 while(r<n)
 {
  printf("%d ",miq.front());//输出窗口内的最小值 
  l++;
  r++;
  if(miq.front_rank()<r-k+1)//判断队首元素是否已经过于陈旧 
  {
   miq.pop_front();//弹出 
  }
  miq.push_back(a[r]);
 }
 cout<<'\n';
  l=0,r=k-1;
 while(r<n)//与上面类似 
 {
  printf("%d ",maq.front());
  l++;
  r++;
  if(maq.front_rank()<r-k+1)
  {
   maq.pop_front();
  }
  maq.push_back(a[r]);
 }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值