【算法】单调队列单调栈

一、单调队列

   用来维护一段区间内的最大值或最小值,例如滑动窗口区间最值等问题。

基本概念

单调队列是一种存储数据的队列,其中元素的顺序是单调递增或单调递减的。在算法竞赛中,我们一般使用两个单调队列,一个维护单调递增序列,另一个维护单调递减序列。单调队列是一个双端队列

代码如下:

#include <iostream>
#include <deque>
#include <vector>
#include <algorithm>
using namespace std;

void output(vector<int>& arr) {
	int n = arr.size(), len = 0;
	for (int i = 0; i < n; i++) len += printf("%3d", i);
	cout << "\n";
	
	for (int i = 0; i < len; i++)printf("-");
	cout << "\n";
	
	for (int i = 0; i < n; i++) len += printf("%3d", arr[i]);
	cout << "\n";
}

int main(){
	int n, k;
	cin >> n >> k;
	vector<int> arr;
	deque<int> q;
	for (int i = 0, a; i < n; i++) {
		cin >> a;
		arr.push_back(a);
	}
	output(arr);
	for (int i = 0; i < n; i++) {
		while (!q.empty() && arr[q.back()] > arr[i])q.pop_back();
		q.push_back(i); //压入下标
		if (i - q.front() == k) q.pop_front(); //弹出队头
		printf("[%d, %d] = arr[%d] = %d \n",
			max(i - k + 1, 0), i,
			q.front(),arr[q.front()]);
	}
}

滑动窗口

154. 滑动窗口 - AcWing题库

滑动窗口是一类问题,需要在一个长度为n的序列中,找到所有长度为k的连续子序列中的最大值或最小值。使用单调队列可以在O(n)的时间复杂度内解决该问题。

具体做法如下:

(1)将前k个元素插入单调队列中,并记录队列的最大值或最小值。
(2)从第k+1个元素开始,每次将一个新的元素插入单调队列中。
(3)插入时,先将队列中所有小于等于该元素的队尾元素出队,保证队列中的元素单调递减。
(4)将该元素插入队尾,并记录队列的最大值或最小值。
(5)将长度为k的子序列的最大值或最小值输出即可。

方法1:(数组实现)

#include <iostream>
using namespace std;

const int N = 1e6 + 10;
int q[N], a[N]; //数组q用来存下标
int main() {
	int n, k;
	cin >> n >> k;
	for (int i = 0; i < n; i++) cin >> a[i];

	//找滑动窗口最小值
	int hh = 0, tt = -1;
	for (int i = 0; i < n; i++) {
		if (i - q[hh] == k) hh++; //队头弹出元素
		while (hh <= tt && a[q[tt]] > a[i]) tt--; //队尾弹出元素
		q[++tt] = i; //压入下标
		if (i - k + 1 >= 0)printf("%d ", a[q[hh]]);
	}
	printf("\n");
	//找滑动窗口最大值
	hh = 0, tt = -1;
	for (int i = 0; i < n; i++) {
		if (i - q[hh] == k) hh++; //队头弹出元素
		while (hh <= tt && a[q[tt]] < a[i]) tt--; //队尾弹出元素
		q[++tt] = i; //压入下标
		if (i - k + 1 >= 0)printf("%d ", a[q[hh]]);
	}
	return 0;
}

方法2:(双端队列)

#include <iostream>
#include <deque>
#include <vector>
using namespace std;

int main() {
	int n, k;
	cin >> n >> k;
	vector<int> arr(n);
	deque<int> q;
	for (int i = 0; i < n; i++)cin >> arr[i];
	for (int i = 0; i < n; i++) {
		if (i - q.front() == k) q.pop_front();
		while (!q.empty() && arr[q.back()] > arr[i])q.pop_back();
		q.push_back(i);
		if (i - k + 1 >= 0) cout << arr[q.front()] << " ";
	}
	cout << endl;

	q.clear();
	for (int i = 0; i < n; i++) {
		if (i - q.front() == k) q.pop_front();
		while (!q.empty() && arr[q.back()] < arr[i])q.pop_back();
		q.push_back(i);
		if (i - k + 1 >=0) cout << arr[q.front()] << " ";
	}
	return 0;
}

区间最值

135. 最大子序和 - AcWing题库

需要在一个长度为n的序列中,找到所有长度为k的子序列中的最大值或最小值。使用单调队列可以在O(n)的时间复杂度内解决该问题。

其实现方法与上面类似,但是需要注意:

  • 区间最值问题是在不限制子序列连续性的情况下,找到子序列中的最大值或最小值。
  • 而滑动窗口问题则是在限制子序列必须连续的情况下,找到所有长度为k的子序列中的最大值或最小值。

方法1:(数组实现)

#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;

typedef long long LL;

const int N = 1e6 + 10;
int q[N];
LL s[N];
int main()
{
	int n, m;
	cin >> n >> m;
	//处理为前缀和序列
	for (int i = 1; i <= n; i++) {
		cin >> s[i];
		s[i] += s[i - 1];
	}
	LL res = -1e10;
	int hh = 0, tt = 0;
	for (int i = 1; i <= n; i++) {
		if (i - q[hh] > m) hh++;
		res = max(res, s[i] - s[q[hh]]);
		while (hh <= tt && s[q[tt]] >= s[i]) tt--;
		
		q[++tt] = i;
	}
	cout << res << "\n";
	return 0;
}

方法2:(双端队列)

#include <iostream>
#include <algorithm>
#include <cstring>
#include <deque>
#include <vector>
#include <algorithm>
using namespace std;
typedef long long LL;

int main()
{
	int n, m;
	cin >> n >> m;
	//处理前缀和
	vector<LL> s(n + 1);
	s.push_back(0);
	deque<LL> q;
	for (int i = 1; i <= n; i++) {
		cin >> s[i];
		s[i] += s[i - 1];
	}
	q.push_back(0);
	LL res = -1e6;
	for (int i = 1; i <= n; i++) {
		if (i - q.front() > m) q.pop_front();
		res = max(res, s[i] - s[q.front()]);
		while (!q.empty() && s[q.back()] >= s[i]) q.pop_back();
		q.push_back(i);
	}
	cout << res << "\n";
	return 0;
}


二、单调栈

基本概念

单调栈实际上就是栈,只是利用了一些巧妙的逻辑,使得每次新元素入栈后,栈内的元素都保持有序(单调递增或单调递减),即从队首不弹出元素的单调队列就是单调栈。

作用:用于找最近小于关系(单调递增)和最近大于关系(单调递减)

代码如下:

#include <iostream>
#include <stack>
#include <vector>
#include <algorithm>
using namespace std;

void output(vector<int>& arr) {
	int n = arr.size(), len = 0;
	for (int i = 0; i < n; i++) len += printf("%3d", i);
	cout << "\n";
	
	for (int i = 0; i < len; i++)printf("-");
	cout << "\n";
	
	for (int i = 0; i < n; i++) len += printf("%3d", arr[i]);
	cout << "\n";
}

int main(){
	int n;
	cin >> n;
	vector<int> arr;
	arr.push_back(-1); //假如极小值为-1
	stack<int> s;
	for (int i = 0, a; i < n; i++) {
		cin >> a;
		arr.push_back(a);
	}
	arr.push_back(-1); //假如极小值为-1
	vector<int> l(arr.size() + 1), r(arr.size() + 1);
	output(arr);

	//右侧
	for (int i = 0;  i < arr.size(); i++) {
		while (!s.empty() && arr[s.top()] > arr[i]) {
			r[s.top()] = i;
			s.pop();
		}
		s.push(i);
	}

	//左侧 (倒着扫描)
	while (!s.empty()) s.pop();
	for (int i = arr.size() - 1; i >= 0; i--) {
		while (!s.empty() && arr[s.top()] > arr[i]) {
			l[s.top()] = i;
			s.pop();
		}
		s.push(i);
	}

	for (int i = 1; i <= n; i++) {
		printf("arr[%d] = %d, right : arr[%d] = %d, left : arr[%d] = %d\n",
			i, arr[i],
			r[i], arr[r[i]],
			l[i], arr[l[i]]);
	}
	return 0;
}

数组实现单调栈:

830. 单调栈

#include <iostream>
using namespace std;
const int N = 10010;

int stk[N], tt ;
int main()
{
	int n;
	cin >> n;
	while(n--)
	{
	    int x;
	    cin>>x;
	    while(tt&&stk[tt]>=x) tt--;
	    if(tt==0) printf("-1 ");
	    else printf("%d ",stk[tt]);
	    stk[++tt]=x;
	}
	return 0;
}

三、总结

单调队列:擅长维护区间【最大/最小】值,最小值对应单调递增队列

单调栈:擅长维护最近【大于/小于】关系,

从左侧先入栈就是维护左侧最近关系

从右侧先入栈,就是维护右侧最近关系。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/786078.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Android约束布局的概念与属性(1)

目录 1&#xff0e;相对定位约束2&#xff0e;居中和偏移约束 约束布局&#xff08;ConstraintLayout&#xff09;是当前Android Studio默认的布局方式&#xff0c;也是最灵活的一种布局方式。约束布局推荐使用所见即所得的模式进行布局&#xff0c;约束布局的大部分布局可以通…

25考研,数二全程跟的张宇老师请问660(做了一半)880和张宇1000题应该怎么选择?

跟张宇老师&#xff0c;也可以做其他的题集&#xff0c;不一定非要做1000题 我当初考研复习的时候&#xff0c;也听了张宇老师的课程&#xff0c;但是我并没有做1000题 因为1000题对于我来说太难了。做了一章之后&#xff0c;就换成其他的题目了。 对于大家来说&#xff0c;…

xcode中对项目或者文件文件夹重命名操作

提起揭秘答案&#xff1a;选中文件后&#xff0c;按下回车键就可以了 如果在项目中对新建的文件夹或者文件名称不满意或者输入错误了&#xff0c;想要修改一下名称该怎么办&#xff1f;如果是在文件或文件夹上右键是没有rename选项的&#xff1a; 其实想要重命名&#xff0c;很…

网络通信、BIO、NIO

1. 涉及的网络基础知识 Socket&#xff1a; 操作系统提供的api&#xff0c;介于应用层和tcp/ip层之间的软件层&#xff0c;封装服务器客户端之间网络通信相关内容&#xff0c;方便调用 IO多路复用&#xff1a; &#xff08;I/O Multiplexing&#xff09;是一种IO操作模式&a…

Java | Leetcode Java题解之第221题最大正方形

题目&#xff1a; 题解&#xff1a; class Solution {public int maximalSquare(char[][] matrix) {int maxSide 0;if (matrix null || matrix.length 0 || matrix[0].length 0) {return maxSide;}int rows matrix.length, columns matrix[0].length;int[][] dp new in…

泰勒雷达图2

matplotlib绘制泰勒雷达图 import matplotlib.pyplot as plt import numpy as np from numpy.core.fromnumeric import shape import pandas as pd import dask.dataframe as dd from matplotlib.projections import PolarAxes import mpl_toolkits.axisartist.floating_axes a…

代码随想录day36

题目一 上边、左边初始化为1 采用求和进行dp运算 class Solution(object):def uniquePaths(self, m, n):""":type m: int:type n: int:rtype: int"""dp [[0]*n for _ in range(m)]for i in range(m):dp[i][0] 1for j in range(n):dp[0][j] 1…

python-课程满意度计算(赛氪OJ)

[题目描述] 某个班主任对学生们学习的的课程做了一个满意度调查&#xff0c;一共在班级内抽取了 N 个同学&#xff0c;对本学期的 M 种课程进行满意度调查。他想知道&#xff0c;有多少门课是被所有调查到的同学都喜欢的。输入格式&#xff1a; 第一行输入两个整数 N , M 。 接…

科普文:一文搞懂jvm实战(四)深入理解逃逸分析Escape Analysis

概叙 Java 中的对象是否都分配在堆内存中&#xff1f; 好了太抽象了&#xff0c;那具体一点&#xff0c;看看下面这个对象是在哪里分配内存&#xff1f; public void test() { Object object new Object(); }这个方法中的object对象&#xff0c;是在堆中分配内存么&#xff1…

利用python进行数据分析 —— python正则表达式(持续更新中!)

文章目录 利用python进行数据分析 —— python基础知识进阶重点笔记&#xff1a;正则表达式re.match 匹配开头re.search 全文匹配re.sub 替换删除re.compile 编译正则findall 返回列表finditer 返回迭代器re.split 分割返回列表(?P...) 分组匹配正则表达符号、修饰符通配符1 ^…

wordpress的restfull API使用教程,之如何用postman调试API,以便能使用vue等前端框架开发主题

文章目录 API开发手册在postman中调试这里以 post 一篇文章为例&#xff0c;讲解如何调试&#xff1a; 步骤 1&#xff1a;生成应用密码步骤 2&#xff1a;配置Postman步骤 3&#xff1a;创建文章 参考链接 API开发手册 官方API手册&#xff1a;https://developer.wordpress.o…

基于AWS Billing Conductor自定义账单计算进行【linker账单】RI/SP还原以及账单菜单栏选择性精细化限制策略设置

文章目录 一、客户需求需求① 设置策略屏蔽billing菜单选项查看需求② 账单RI和SP还原及SP和RI的共享 二、AWS Billing Conductor介绍三、IAM 精细操作映射参考四、详细步骤操作演示4.1 AWS Organization策略设置4.2 账单和成本管理设置4.3 AWS Billing Conductor设置4.3.1 创建…

文档图像处理:大模型的突破与新探索

前言 随着数字化时代的到来&#xff0c;文档图像处理技术在各行各业扮演着越来越重要的角色。在2023第十二届中国智能产业高峰论坛&#xff08;CIIS 2023&#xff09;的专题论坛上&#xff0c;合合信息智能技术平台事业部副总经理、高级工程师丁凯博士分享了当前文档图像处理面…

MSPM0G3507——时钟配置(与32关系)

先将32端时钟配置分为1&#xff0c;2&#xff0c;3如图 1是PSC左边未经分频的时钟源&#xff08;HZ&#xff09; 2是经过PSC分频的时钟信号&#xff08;HZ&#xff09; 3是最终的输出信号&#xff08;HZ&#xff09; 3输出的是一个定时器周期的HZ&#xff0c;可以转换成时间 …

ThreeJS-3D教学十五:ShaderMaterial(noise、random)

ThreeJS-3D教学十四:ShaderMaterial(length、fract、step) 上面这篇主要是操作 fragmentShader 片元着色器,实现对物体颜色的修改,这次咱们来看下修改 vertexShader 顶点着色器,这个其实就是位移各个顶点的位置。 接下来我们先介绍下 noise 噪声函数(Perlin Noise、Sim…

linux权限深度解析——探索原理

前言&#xff1a;本节内容主要讲述的是linux权限相关的内容&#xff0c; linux的权限如果使用root账号是感受不到的&#xff0c; 所以我们要使用普通账号对本节相关内容进行学习&#xff0c;以及一些实验的测试。 然后&#xff0c; 通过linux权限的学习我们可以知道为什么有时候…

第33讲:K8S集群StorageClass使用Ceph CSI供应商与Cephfs文件系统集成

文章目录 1.Ceph CSI供应商简介2.创建Cephfs文件系统为StorageCLass提供底层存储端2.1.创建Cephfs文件系统2.2.在Cephfs文件系统中为Storageclass创建子目录2.3.在Cephfs文件系统中创建一个子卷 3.在K8S集群中部署Cephfs-CSI供应商客户端3.1.下载Cephfs-CSI客户端的资源编排文件…

08.C2W3.Auto-complete and Language Models

往期文章请点这里 目录 N-Grams: OverviewN-grams and ProbabilitiesN-gramsSequence notationUnigram probabilityBigram probabilityTrigram ProbabilityN -gram probabilityQuiz Sequence ProbabilitiesProbability of a sequenceSequence probability shortcomingsApproxi…

Spring最早的源码

地址&#xff1a;Spring最早的源码

顶顶顶顶顶顶顶顶顶顶顶顶顶顶顶顶顶顶顶

欢迎关注博主 Mindtechnist 或加入【智能科技社区】一起学习和分享Linux、C、C、Python、Matlab&#xff0c;机器人运动控制、多机器人协作&#xff0c;智能优化算法&#xff0c;滤波估计、多传感器信息融合&#xff0c;机器学习&#xff0c;人工智能等相关领域的知识和技术。关…