滑动窗口模板

滑动窗口模板

基本概念

滑动窗口是一种基于双指针的一种思想,两个指针指向的元素之间形成一个窗口。

分类:窗口有两类,一种是固定大小类的窗口,一类是大小动态变化的窗口。

什么情况可以用滑动窗口来解决实际问题呢?

  • 一般给出的数据结构是数组或者字符串
  • 求取某个子串或者子序列最长最短等最值问题或者求某个目标值时
  • 该问题本身可以通过暴力求解
核心思路

思路来源:滑动窗口 c++_滑动窗口c+±CSDN博客

一个简单的思路,就是在每一个滑动窗口中,遍历一次滑动窗口中的数,找到滑动窗口中的最大值和最小值,并用两个数组把它们存起来。若数组长度为n,滑动窗口的长度k,则有n-k+1个滑动窗口。

那有什么可以减少比较的次数吗?我们发现每次滑动窗口时,只有一个数发生了改变即原滑动窗口左端元素被推出,而窗口右端进入一个新元素。如果标记原窗口最大最小值及其位置,那么在新的窗口中,若最大最小值仍在新窗口中,则只需要拿新元素和原来的最大最小值比较并更新,若最大或最小值不在新窗口中,我们再遍历一次窗口。
实现代码:

//滑动窗口的模板 
#include<bits/stdc++.h>
#define int long long
#define endl "\n"
using namespace std;
const int N=1e6+5;
int a[N],q[N];//定义两个数组,a数组用来存入数,q数组用来存放下标 
int hh=0,tt=-1; 
signed main(){
	ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
	int n,k;
	cin >> n >> k;
	for(int i=0;i<n;++i) cin >> a[i];
	for(int i=0;i<n;++i){
		if(hh<=tt && q[hh]<i-k+1) hh++;//判断最小值是否还在窗口,不在则推出 
		while(hh<=tt && a[q[tt]]>=a[i]) tt--;//如果队尾元素比新元素大,则推出 
		q[++tt]=i;//标记当前下标的位置 
		if(i>=k-1) cout << a[q[hh]] << " ";
	}
	cout << endl;
	hh=0,tt=-1;
	for(int i=0;i<n;++i){
		if(hh<=tt && q[hh]<i-k+1) hh++;//判断最大值同理 
		while(hh<=tt && a[q[tt]]<=a[i]) tt--;//队尾元素比新元素小,则推出 
		q[++tt]=i;
		if(i>=k-1) cout << a[q[hh]] << " ";
	}
	return 0; 
} 

如上代码是模拟双端队列实现的,时间复杂度比双端队列快了一倍。

双端队列的代码也奉上:

#include<bits/stdc++.h>
#define int long long
#define endl "\n"
using namespace std;
const int N=1e6+5;
int a[N];
deque<int> q;
signed main(){
	//ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
	int n,k;
	cin >> n >> k;
	for(int i=0;i<n;++i) cin >> a[i];
	for (int i = 0; i < n; i++) {
        if (!q.empty() && q.front() < i - k + 1) q.pop_front();
        while(!q.empty() && a[q.back()] >= a[i]) q.pop_back();
        q.push_back(i);
        if (i >= k - 1) printf("%d ", a[q.front()]);
    }
    cout << endl;  
    q.clear();
    for (int i = 0; i < n; i++) {
        if (!q.empty() && q.front() < i - k + 1)q.pop_front();
        while(!q.empty() && a[q.back()] <= a[i]) q.pop_back();
        q.push_back(i);
        if (i >= k - 1) printf("%d ", a[q.front()]);
    }
    return 0;
} 
  • 22
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值