单调队列(C/C++)

引言:

单调队列和单调栈都是一种数据结构,应用十分广泛,在蓝桥杯、ICPC、CCPC等著名编程赛事都是重点的算法,今天博主将自己对单调栈与单调队列的理解以及刷题的经验,用一篇博客分享给大家,希望对大家有所帮助,它们用于解决类似“寻找最大值与最小值”这样的问题。它们的区别在于如何维护数据的单调性。

  1. 单调栈(Monotonic Stack):

    • 单调栈是一种栈数据结构,只能在栈顶进行插入和删除操作。
    • 单调栈的特点是栈中的元素按照一定的单调性排列,常用的有单调递增和单调递减。
    • 在插入新元素时,如果新元素破坏了当前的单调性,则从栈顶删除一部分元素,直到满足单调性要求。这样可以保证栈中的元素保持单调性。
    • 单调栈的典型应用是在寻找下一个更大/更小元素的问题。
  2. 单调队列(Monotonic Queue):

    • 单调队列是一个双端队列,支持在队列两端进行插入和删除操作。
    • 单调队列的特点是队列中的元素按照一定的单调性排列,常用的有单调递增和单调递减。
    • 在插入新元素时,如果新元素破坏了当前的单调性,则在队尾删除一部分元素,直到满足单调性要求。这样可以保证队列中的元素保持单调性。
    • 单调队列的典型应用是在滑动窗口中寻找最大/最小值的问题。

单调队列和单调栈都是用于维护数据的单调性,但单调队列是双端队列,用于在滑动窗口中寻找最大/最小值,而单调栈是栈数据结构,用于寻找下一个更大/更小元素

接上篇单调栈,下面我们对单调队列进行深度解析


单调队列:

单调队列是一种特殊的队列数据结构,用于解决一些与序列相关的问题。单调队列中的元素按照其值的大小有序排列,同时还满足队列的先进先出的性质。

单调队列的主要特点是,队列中的元素始终保持一个单调性,可以是递增或递减。这意味着,当有新的元素入队时,队列会自动进行调整,将不符合要求的元素删除。

单调队列的应用场景主要是解决滑动窗口相关的问题。滑动窗口是指在一个序列中,窗口以固定大小向右滑动,每次滑动一个位置。在每个滑动窗口中,需要对窗口中的元素进行一些操作。

使用单调队列可以在O(n)的时间复杂度内解决滑动窗口问题。具体的操作过程如下:

  1. 首先,将窗口的前k个元素依次入队;
  2. 与队列尾部的元素进行比较,将比当前元素小的元素从队列尾部依次出队,直到队列尾部的元素大于等于当前元素,或者队列为空;
  3. 判断队列的头部元素是否已经超出了滑动窗口的范围,如果超出了,则将队列头部元素出队;
  4. 将当前元素入队;
  5. 对每个滑动窗口,都可以通过队列的头部元素获取到当前窗口的最大(最小)值。

总之,单调队列是一种高效解决滑动窗口问题的数据结构,它可以在O(n)的时间复杂度内完成操作。同时,单调队列也有一些其他的应用场景,如求滑动窗口中的最小值、最大值等。

如下图:滑动窗口为3,模拟单调队列入队可以一下过程。

单调队列是一种特殊的队列,它可以在 O(1) 时间内完成以下两种操作:

1. 在队尾插入元素 x。
2. 在队头删除元素。

单调队列常用于求解滑动窗口中的最值问题,例如求最大值、最小值或其他满足特定条件的值。

下面是一些单调队列的具体应用场景

1. 求滑动窗口的最大值/最小值:给定一个数组 nums 和一个滑动窗口的大小 k,需要找出每个滑动窗口里的最大值或最小值。使用单调队列可以在 O(n) 时间内解决这个问题。

2. 求滑动窗口中的最大值与最小值的差值不超过一个给定值的个数:给定一个数组 nums、一个滑动窗口的大小 k,以及一个给定值 maxDiff,需要找出滑动窗口中最大值与最小值的差值不超过 maxDiff 的窗口个数。

3. 求滑动窗口中的最大子数组和:给定一个数组 nums 和一个正整数 k,需要找出滑动窗口中长度为 k 的连续子数组的最大和。

4. 求滑动窗口中的第 k 大的元素:给定一个数组 nums 和一个正整数 k,需要找出滑动窗口中第 k 大的元素。

这些是单调队列的应用场景之一,还有其他一些问题也可以使用单调队列解决。单调队列的核心思想是维护一个递增或递减的队列,通过在队尾插入元素和在队头删除元素来保持队列的单调性。这样就可以在 O(1) 时间内获取滑动窗口中的最值或其他满足条件的值。


模板奉上:

第一种使用STL
        deque<int>q;//滑动窗口
        for(int i = 0; i < nums.size(); i++){
        while(!q.empty()&&nums[q.back()]<nums[i])//维护队列单调性(递增)
                q.pop_back();
            q.push_back(i);//入队
            if(!q.empty() && i-q.front() >= k)//判断队头是否需要出队
                q.pop_front();
            if(i >= k-1){
                //在此处寻找最大值,以及代码扩展
                res.push_back(nums[q.front()]);//取队头作为窗口最大元素
            }
        }
        return res;
第二种自己手写一个
        int h=-1,t=0;
        int q[1005];
        vector<int> res;
        for(int i=0;i<nums.size();i++){
            while(h<=t&&a[q[t]]<=nums[i])t--;//队尾不满足单调性出队
            q[++t]=i;//入队
            if(q[h]<i-k+1)h++;//队头超出窗口,出队
            if(i-k+1>=0) res.push_back(nums[q[h]]);//此处代码扩展
        }
        return res;


实战演练——单调队列习题

滑动窗口最大值

79. 滑动窗口的最大值 - AcWing题库高质量的算法题库icon-default.png?t=N7T8https://www.acwing.com/problem/content/description/75/

class Solution {
public:
    vector<int> maxInWindows(vector<int>& nums, int k) {
        int q[100001];
        int hh=0,tt=0;
        vector<int> ans;
        for(int i=0;i<nums.size();i++)
        {
            while(hh<=tt&&nums[q[tt]]<=nums[i]) tt--;//新元素不满足单调递增,队尾出队
            q[++tt]=i;//新元素入队
            if(q[hh]<i-k+1) hh++;//队头不再窗口内
            if(i-k+1>=0) ans.push_back(nums[q[hh]]);//窗口内元素个数大于等于k,可进行操作
        }
        return ans;
    }
};

滑动窗口【模板】 

滑动窗口 /【模板】单调队列 - 洛谷icon-default.png?t=N7T8https://www.luogu.com.cn/problem/P1886

使用双端队列实现(deque):
#include<bits/stdc++.h>
#define N 1000005
using namespace std;
int n,k;
int a[N];
deque<int> q;//使用双端队列
int main()
{
	cin>>n>>k;
	for(int i = 1;i<=n;i++)
		cin>>a[i];
	
	for(int i = 1;i<=n;i++)//求滑动窗口最小值
	{
		while(q.size() && q.back()>a[i])
			q.pop_back();
		
		q.push_back(a[i]); 
		if(i-k>=1 && q.front()== a[i-k])
			q.pop_front();
		
		if(i>=k)
			cout<<q.front()<<" "; 
		
	}
	cout<<"\n";
	q.clear();//清空防止影响求最大值
	
	for(int i = 1;i<=n;i++)//求滑动窗口最大值
	{
		while(q.size() && q.back()<a[i])
			q.pop_back();
		q.push_back(a[i]);
		
		if(i-k>=1 && q.front() == a[i-k])
			q.pop_front();
		
		if(i>=k)
			cout<<q.front()<<" ";
	}
	return 0;
}
手写一个:
#include<iostream>
using namespace std;
const int N=1e6+5;
int h,t;
int n,k;
int a[N],q[N];
int main(){
	cin>>n>>k;
	for(int i=0;i<n;i++){
		cin>>a[i];
	}
	h=0,t=-1;
	for(int i=0;i<n;i++){
		while(h<=t&&a[q[t]]>=a[i])t--;
		q[++t]=i;
		if(q[h]<i-k+1)h++;
		if(i-k+1>=0)cout<<a[q[h]]<<" ";
	}
	cout<<endl;
	h=0,t=-1;
	for(int i=0;i<n;i++){
		while(h<=t&&a[q[t]]<=a[i])t--;
		q[++t]=i;
		if(q[h]<i-k+1)h++;
		if(i-k+1>=0)cout<<a[q[h]]<<" ";
	}
	return 0;
}

多重背包问题(单调队列优化)

6. 多重背包问题 III - AcWing题库高质量的算法题库icon-default.png?t=N7T8https://www.acwing.com/problem/content/6/这里可以看一下我专门写的多重背包问题的博客,这里不再介绍

细谈多重背包问题-CSDN博客


总结:

单调队列是一种非常有用的数据结构,可以高效地解决需要维护窗口内最值的问题。使用单调队列的时间复杂度为O(n),其中n为输入数组的长度。其实现方式有双向队列和单调栈两种,根据具体问题的要求选择适合的实现方式即可,文章尚有不足,恳请各位大佬指出,博主不胜感激,感谢大家支持。

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

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

相关文章

第七、八章 函数 + 文件

第七章 函数 多个返回值 def test_return():return 1, "hello", Truex,y,z test_return() print(x) print(y) print(z) 1 hello True 传入的参数 位置参数 定义&#xff1a;调用函数时根据函数定义的参数位置来传递参数要求&#xff1a;传递的参数和定义的参数的顺…

1.C++入门

1.关键字&#xff08;C98&#xff09; 2.命名空间 在 C/C 中&#xff0c;变量、函数和后面要学到的类都是大量存在的&#xff0c;这些变量、函数和类的名称将都存 在于全局作用域中&#xff0c;可能会导致很多冲突。使用命名空间的目的是 对标识符的名称进行本地化 &#xff…

利用 Amazon ECS 进行分布式机器学习

本文作者 Santiago Flores Kanter 亚马逊云科技高级解决方案架构师 Ravi Yadav 亚马逊云科技首席容器专家 校译作者 梁宇 亚马逊云科技专业服务团队 DevOps 顾问 在 Amazon ECS 服务上运行分布式机器学习工作负载可让 ML 团队更加专注于创建、训练和部署模型&#xff0c;而不是…

搭建PyTorch神经网络进行气温预测(手写+调包两种方法)(保证学会!)+找到神经网络的最优情况

代码上有注释&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01; 本篇主要包括三大部分&#xff1a; 第一部分&#xff1a;导入数据集导入第三方库数据集简单介绍与可视化数据集简单预处理 第二部分&#xff1a;手写神经网络代码实现气温预测&#…

链表传一级指针以及leetcode做题有感

上个文章说要传二级指针&#xff0c;经过一段时间的学习之后才知道可以传一级指针&#xff1a; 之所以要传二级指针&#xff0c;是要改变一级指针的值&#xff0c;也就是把头节点的指针改变&#xff0c;如图&#xff1a; 从左边到右边&#xff0c;头指针 一级指针plist 的值发…

C++算法题 - 哈希表

目录 383. 赎金信205. 同构字符串290. 单词规律242. 有效的字母异位词49. 字母异位词分组1. 两数之和202. 快乐数219. 存在重复元素Ⅱ128. 最长连续序列 383. 赎金信 LeetCode_link 给你两个字符串&#xff1a;ransomNote 和 magazine &#xff0c;判断 ransomNote 能不能由 m…

道合顺传感新品上市!高性能氢气传感器DSB14-G3K-J详解

道合顺传感高性能氢气传感器DSB14-G3K-J正式发布&#xff01;超强抗干扰能力优势明显。应对氢气安全挑战、高性能氢气传感器国产化、为储能保驾护航。 氢气&#xff0c;作为现今能源领域中的新贵&#xff0c;在储能行业中应用广泛且备受瞩目。但氢气易燃、易爆特性使其在生产、…

gradle.properties 中文字符乱码问题

我用AS开发Android应用。在gradle.properties中输入中文&#xff0c;再次打开时&#xff0c;发现中文变成了&#xff1f;&#xff1f;&#xff1f;。上网查询&#xff0c;发现了一个解决办法&#xff1a; 在菜单File-Settings-Editor-File Encodings中&#xff0c;将“Default…

【复习笔记】FreeRTOS(五)时间片调度

本文是FreeRTOS复习笔记的第五节&#xff0c;时间片调度。 上一篇文章&#xff1a; 【复习笔记】reeRTOS(四) 列表项的插入和删除 文章目录 1.时间片调度简介1.1. 运行过程 二、实验设计三、测试例程四、实验效果 1.时间片调度简介 FreeRTOS支持多个任务同时拥有一个优先级&am…

春藤实业启动SAP S/4HANA Cloud Public Edition项目,与工博科技携手数字化转型之路

3月11日&#xff0c;广东省春藤实业有限公司&#xff08;以下简称“春藤实业”&#xff09;SAP S/4HANA Cloud Public Edition&#xff08;以下简称“SAP ERP公有云”&#xff09;项目正式启动。春藤实业董事长陈董、联络协调项目经理慕总、内部推行项目经理陈总以及工博董事长…

数仓建模—数据架构

数仓—数据架构 为了在企业决策中使用数据,数据必须经过整个数据平台的各个阶段。整个过程是什么样子的,从开始到结束?原始形式的数据是如何转化为可导致商业决策的见解的?这些问题可以通过数据架构来回答。 数据架构是指记录组织所有数据资产的模型、规则和标准。它映射…

Web前端-JavaScript

黑马程序员JavaWeb开发教程 文章目录 一、js引入方式1、内部脚本2、外部脚本 二、js基础语法1、书写语法&#xff08;1&#xff09;基本语法&#xff08;2&#xff09;输出语句 2、变量&#xff08;1&#xff09;变量&#xff08;2&#xff09;注意事项 3、数据类型、运算符、流…

spring-数据处理及跳转

结果跳转方式 ModelAndView 设置ModelAndView对象 , 根据view的名称 , 和视图解析器跳到指定的页面 . 页面 : {视图解析器前缀} viewName {视图解析器后缀} <!-- 视图解析器 --> <bean class"org.springframework.web.servlet.view.InternalResourceViewRes…

Java定时任务

一、java.util.Timer java.util.Timer 类允许您在未来的某个时间执行一个任务&#xff0c;或者在一定的时间间隔执行任务。您可以创建一个 Timer 实例&#xff0c;并调用其 schedule() 方法来安排任务的执行。这种方式比较简单&#xff0c;但在高并发环境下可能不够灵活。 1.…

Git学习与码云实战

Git学习与码云实战 Git安装 概述&#xff1a; Git 是一个开源的分布式版本控制系统&#xff0c;可以有效、高速的处理从很小到非常大的项目版本管理&#xff0c;是目前使用范围最广的版本管理工具。 下载安装&#xff1a; 下载地址&#xff1a;https://git-scm.com/ 下载后傻瓜…

李彦宏:开源模型会越来越落后

李彦宏&#xff1a;开源模型会越来越落后 昨天听完的李总讲座 大家以前用开源觉得开源便宜&#xff0c;其实在大模型场景下&#xff0c;开源是最贵的。所以&#xff0c;开源模型会越来越落后。 ——李彦宏 至于开源还是闭源&#xff0c;这和企业的利益息息相关。 随着科技的迅猛…

双向链表详解

一.双向链表结构 我们一般所说的双向链表是带头循环双向链表&#xff0c;这里的带头更我们之前的头节点不是一回事。带头链表里的头节点&#xff0c;实际上为哨兵位&#xff0c;哨兵位的头节点种是不存放任何有效数据的&#xff0c;只是站在这里起到放哨的作用。 哨兵位的意义…

【C++从练气到飞升】08---模板

&#x1f388;个人主页&#xff1a;库库的里昂 ✨收录专栏&#xff1a;C从练气到飞升 &#x1f389;鸟欲高飞先振翅&#xff0c;人求上进先读书。 目录 一、泛型编程 什么是泛型编程: 二、函数模板 1. 函数模板概念 2. 函数模板格式 3. 函数模板的原理 4. 函数模板的实例…

Nginx part2.2

目录 如何用Nginx搭建多网址服务器&#xff1f; 基于ip地址的虚拟主机 1. 先建立存储网页的目录 2.进行子配置 3.编写.conf文件 基于端口号的虚拟主机 基于域名的虚拟主机 如何用Nginx搭建多网址服务器&#xff1f; 有些网站&#xff0c;ip不同&#xff0c;域名不同&…

格林兰岛和南极洲的流域边界文件下载

&#xff08;1&#xff09;南极流域系统边界和掩蔽区 下图显示了由戈达德冰面高程小组使用ICESat数据开发的南极分水岭。我们对西南极冰盖&#xff08;系统18-23和1&#xff09;、东南极冰盖&#xff08;系统2-17&#xff09;和南极半岛&#xff08;系统24-27&#xff09;的定…