单调队列模板

滑动窗口 /【模板】单调队列 - 洛谷

本题大意是给出一个长度为 n 的数组,编程输出每 k 个连续的数中的最大值和最小值。

使用单调队列可将复杂度优化到O(n)

例如[3,2,2,1,4,2]的最大值,始终把最大值放到头部位置

首先3进队列;q={3}

然后2,因为q最后一个大于2,2进队列.q={3,2}

然后2,因为q最后一个等于2,要弹出队尾,q={3},3比2大,2进,q={3,2}

然后1,q={3,2,1}

然后4,4比这几个数字都要大,弹出q={4}

while (head <= tail && a[q[tail]] <= a[i]) tail--;
q[++tail] = i;

head<=tail 表示队列有元素 a[q[tail]]>=a[i]表示队尾元素小于新的

这个循环让队列依次弹出1 2 3然后4入队

这样我们就可以使得k区间的最大值 始终是a[q[head]]

具体的操作是:
定义一个数组a[N]:用来存放原始数据

定义一个数组q[n]:表示队列.(deque)

获取最小值:

//代表q没有元素
int head = 0, tail = -1;
//先形成一个长度为(k-1)的窗口
for (int i = 1; i < k; i++) {
	//存在元素 尾巴元素大于新来的 弹出尾巴
	while (head <= tail && a[q[tail]] >= a[i]) tail--;
	//入队(切记入队的是数组下标)
	q[++tail] = i;
}

解释一下为什么要先形成一个(k-1)的窗口:

对于这个序列,很明显是求划线区间的最小值

像下面一样形成(k-1)的窗口 就可以做到窗口向右滑 得到一个答案 窗口向右滑 又得到一个答案

形成这个窗口后:

//刚才形成(k-1)的窗口 现在来到k位置
for (int i = k; i <= n; i++) {
	//存在元素 尾巴元素大于新来的 弹出尾巴
	while (head <= tail && a[q[tail]] >= a[i]) tail--;
	//入队(切记入队的是数组下标)
	q[++tail] = i;
	//因为q[head]表示下标
	while (q[head] <= i - k) head++;
	cout << a[q[head]] << " ";
}

完整代码:

#include<iostream>
#include<algorithm>
#include<cstdio>
using namespace std;
const int N = 1E6 + 10;
//注意q数组存的是下标
int q[N];
int a[N];
int n, k;
void getmin() { 
	//代表q没有元素
	int head = 0, tail = -1;
	//先形成一个长度为(k-1)的窗口
	for (int i = 1; i < k; i++) {
		//存在元素 尾巴元素大于新来的 弹出尾巴
		while (head <= tail && a[q[tail]] >= a[i]) tail--;
		//入队(切记入队的是数组下标)
		q[++tail] = i;
	}
	//刚才形成(k-1)的窗口 现在来到k位置
	for (int i = k; i <= n; i++) {
		//存在元素 尾巴元素大于新来的 弹出尾巴
		while (head <= tail && a[q[tail]] >= a[i]) tail--;
		//入队(切记入队的是数组下标)
		q[++tail] = i;
		//因为q[head]表示下标
		while (q[head] <= i - k) head++;
		cout << a[q[head]] << " ";
	}
}
void getmax() { 
	int head = 0, tail = -1;
	for (int i = 1; i < k; i++) {
		while (head <= tail && a[q[tail]] <= a[i]) tail--;
		q[++tail] = i;
	}
	for (int i = k; i <= n; i++) {
		while (head <= tail && a[q[tail]] <= a[i]) tail--;
		q[++tail] = i;
		while (q[head] <= i - k) head++;
		cout << a[q[head]] << " ";
	}
}
int main()
{
	cin >> n >> k;
	for (int i = 1; i <= n; i++)cin >> a[i];
	getmin();
	cout << endl;
	getmax();
	cout << endl;
	return 0;
}

  • 5
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值