单调队列(超级详细讲解)

目录

单调队列定义:

单调队列分两种,

解题模板

题意分析:主要是寻找滑动窗口的最大值与最小值

此题核心代码分三步走:

1.记得写输入还有题意的答案递推代码

2. 维护窗口的大小

3移除尾部元素

洛谷原题

P1886 滑动窗口 /【模板】单调队列

模拟双端队列也就是窗口的移动过程

单调队列解题思路

题意分析:主要是寻找滑动窗口的最大值与最小值

此题核心代码分三步走:

这是c++风格代码

这是c语言风格代码,也就是通过数组来模拟这件事,思路与上面一样,我们是通过数组来完成这件事

思路是一样的,数组模拟只是加了个下标数组min[],还有max[]


如果你觉得本文还可以,请留下你的,谢谢!(这是我第二次学习单调队列可以说是复习)

我之前是通过看视频,还有代码网站学会的,个人觉得还不错,非常推荐

单调队列正式登场!| LeetCode:239. 滑动窗口最大值_哔哩哔哩_bilibili

代码网站

代码随想录

单调队列 - OI Wiki

https://zhuanlan.zhihu.com/p/437438743

单调队列(超级详细讲解)//这是我的语雀,但是复制过来字体的颜色不见了,后面手动加了点

单调队列定义:

顾名思义,单调队列的重点分为「单调」和「队列」。

单调」指的是元素的「规律」——递增(或递减)。

队列」指的是元素只能从队头和队尾进行操作。

单调队列分两种

一种是单调递增的,另外一种是单调递减的。

解题模板

题意分析:主要是寻找滑动窗口的最大值与最小值

此题核心代码分三步走:
1.记得写输入还有题意的答案递推代码
qmax.push_back(i);
		if (i >= k)cout << a[qmax.front()] << " ";

2. 维护窗口的大小

(这个一般是与双端队列的头部比较)弹出的一般也是头部)

如果队列中的某个元素的下标 qmin[head_min] 小于等于 i - k,说明这个元素已经不在当前窗口内,需要被移除。

while (!qmin.empty() && qmax.front() <= i - k) {
			qmax.pop_front();
}

3移除尾部元素

(如果当前滑动窗口是求最小值)

如果队列不为空且当前元素与队尾元素小,移除窗口中比首元素大的

(如果当前滑动窗口是求最大值)

如果队列不为空且当前元素与队尾元素大,移除窗口中比首元素小的

//窗口 1 2 5 来了一个3变成 1 2 3 (这里是求最大值)

while (!qmax.empty() && a[qmax.back()] <= a[i]) {
			qmax.pop_back();
}

洛谷原题

P1886 滑动窗口 /【模板】单调队列

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

模拟双端队列也就是窗口的移动过程

单调队列解题思路

该类型多半采用单调队列,暴力容易超时,当时暴力写出来超时,才来用单调队列

主要是理解窗口移动元素也就是双端队列移动

本来本文是打算插入动画的,但是我没有语雀的会员插入不了,抱歉了,各位大佬

下面这是个博主实操演示过程

链接单调队列正式登场!| LeetCode:239. 滑动窗口最大值_哔哩哔哩_bilibili

题意分析:主要是寻找滑动窗口的最大值与最小值

此题核心代码分三步走:

1.记得写输入还有题意的所需要的答案

    qmax.push_back(i);//输入
		if (i >= k)cout << a[qmax.front()] << " ";//

2. 维护窗口的大小

(这个一般是与双端队列的头部比较)弹出的一般也是头部)

如果队列中的某个元素的下标 qmin[head_min] 小于等于 i - k,说明这个元素已经不在当前窗口内,需要被移除。

while (!qmin.empty() && qmin.front() <= i - k) {
			qmin.pop_front();
}

3移除尾部元素

(如果当前滑动窗口是求最小值)如果队列不为空且当前元素与队尾元素小,移除队尾元素

(如果当前滑动窗口是求最大值)如果队列不为空且当前元素与队尾元素大,移除队尾元素

//窗口 1 2 5 来了一个3变成 1 2 3

while (!qmin.empty() && a[qmin.back()] >= a[i]) {
			qmin.pop_back();
		}

这是c++风格代码

c++主要思路是通过窗口期的头尾判断来解决这个问题,,max窗口期的前面是最大值,min窗口期的前面是最小值

窗口期的后面要与a[i]作比较

#include<bits/stdc++.h>
using namespace std;
const int ma = 1000005;
int n, k;
int a[ma];
deque<int>qmin, qmax;
int main() {
	cin >> n >> k;
	for (int i = 1; i <= n; i++) {
		cin >> a[i];
	}
	for (int i = 1; i <= n; i++) {
		//按下标来,if数组首地址小于等于i-k即0即,当前元素不在窗口期
		while (!qmin.empty() && qmin.front() <= i - k) {
			qmin.pop_front();
		}
		//窗口  1 2 5  来了一个3变成   1  2  3
		while (!qmin.empty() && a[qmin.back()] >= a[i]) {
			qmin.pop_back();
		}
		qmin.push_back(i);
		if (i >= k)  cout << a[qmin.front()] << " ";
	}
	cout << endl;
	for (int i = 1; i <= n; i++) {
		//不在窗口期的丢了
		while (!qmax.empty() && qmax.front() <= i - k) {
			qmax.pop_front();
		}
		//移除队列中比首元素小的
		while (!qmax.empty() && a[qmax.back()] <= a[i]) {
			qmax.pop_back();
		}
		qmax.push_back(i);
		if (i >= k)cout << a[qmax.front()] << " ";

	}
	return 0;
}
这是c语言风格代码,也就是通过数组来模拟这件事,思路与上面一样,我们是通过数组来完成这件事
思路是一样的,数组模拟只是加了个下标数组min[],还有max[]
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#define ma 1000003
int n, a[ma], t, h, k, min[ma], max[ma];
//min求最小值的所有
//max求最大值的所有
int main() {
	scanf("%d%d", &n, &k);
	for (int i = 1; i <= n; i++)
		scanf("%d", &a[i]);
	h = t = 1;
	//h  min//头指针
	//t  max//尾指针
	for (int i = 1; i <= n; i++) {
		//当前首元素不在窗口期
		if (h < t && min[h] <= i - k) {
			h++;
		}
		//窗口期  125  123
		while (h<t && a[min[t - 1]]>=a[i]) {
			t--;
		}
		min[t++] = i;
		if (i >= k)printf("%d ", a[min[h]]);
	}
	printf("\n");
	h = t = 1;
	for (int i = 1; i <= n; i++) {
		if (h < t && max[h] <= i - k) {
			h++;
		}
		while (h < t && a[max[t-1]] <= a[i]) {
			t--;
		}
		max[t++] = i;
		if (i >= k)printf("%d ", a[max[h]]);
	}
	return 0;
}


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值