单调栈与单调队列

  • 一、前言
  • 二、单调栈
    • 1、单调栈的概念与实现方式
    • 2、单调栈代码实现
  • 三、单调队列
    • 1、单调队列的概念与实现方式
    • 2、单调队列代码实现

一、前言

在学习了最基本的栈和队列的知识点后,我们可以通过栈和队列解决一些更加复杂的问题,也可以通过栈与队列来优化一些算法的时间复杂度。在本博客中,笔者将介绍一下单调栈与单调队列的概念与实现方法。

二、单调栈

1、单调栈的概念与实现方式

与栈不同,单调栈中的元素必须保证某种单调性,可以是单调递增,也可以是单调递减。当然,为了保证栈中的元素单调,我们需要在入栈时进行一定的操作。

  • 例如,假设此时单调递增栈中的元素为s={2,6,8,9},即将入栈的元素t=7。
  • 我们发现7小于9,如果7入栈会导致不单调。因此我们把9pop出栈。
  • 然后,我们发现7依然小于栈顶元素8,重复上文操作
  • 此时,我们终于发现栈顶元素6小于7,因此把7放进栈中
  • 同理,如果我们再把t=9准备入栈,此时发现9大于7,直接入栈
  • 然后我们如果再让t=1,操作后单调栈就会变成这样

2、单调栈代码实现

例题链接:Acwing 单调栈

给定一个长度为 N 的整数数列,输出每个数左边第一个比它小的数,如果不存在则输出 −1。输入格式
第一行包含整数 N,表示数列长度。第二行包含 N 个整数,表示整数数列。输出格式
共一行,包含 N 个整数,其中第 i 个数表示第 i 个数的左边第一个比它小的数,如果不存在则输出 −1。数据范围
1≤N≤1e5
1≤数列中元素≤1e9
输入样例:
5
3 4 2 7 5
输出样例:
-1 3 -1 2 2

分析:我们发现,对于第i个数的答案,我们只要把第1个数到第i个数进行单调递增栈的入栈操作,那么答案就是第i个数入栈前的栈顶元素。如果我们在对第i个数进行入栈操作时发现所有的数都被pop出去了,那么就说明答案不存在。

那么为什么是这样呢?首先我们发现如果要成为答案,必须满足两个条件,小与近。假设两个数a>b,如果a出现在b的左边,那么a 无论如何都不可能成为答案 的,这样的数留在栈里就没有意义。只有当a在b的右边并且第i个数大于a,a才可能成为答案。

正确代码:

//#pragma GCC optimize(2)
#include<iostream>
#include<iomanip>
#include<cstdio>
#include<string>
#include<algorithm>
#include<cmath>
#include<queue>
#include<vector>
#include<map>
#include<stack>
#include<set>
#include<bitset>
#include<ctime>
#include<cstring>
#include<list>
#define ll long long
#define ull unsigned long long
#define INF 0x3f3f3f3f
#define mem(a,b) memset(a,b,sizeof(a))
using namespace std;
typedef  pair<int, int> PII;
const int N = 1e6 + 7;int n;
int a[N];  //储存答案
stack<int>s;  //单调递增栈void solve()
{cin >> n;for (int i = 1; i <= n; i++){int t;cin >> t;  //输入元素while (s.size())  //实现单调栈{if (s.top() < t)  //如果栈顶小于t{a[i] = s.top();  //栈顶就是答案break;  //结束单调栈实现操作}elses.pop();  //否则把栈顶丢出去,因为现在的栈顶又比t大又在t左边,是不可能成为后面的数的答案的}if (s.empty()) a[i] = -1;  //如果栈被掏空了,就说明没有答案s.push(t);  //无论有没有答案都要把t入栈}for (int i = 1; i <= n; i++)  //输出答案cout << a[i] << ' ';
}int main()
{std::ios::sync_with_stdio(false);cin.tie(0), cout.tie(0);solve();return 0;
}

三、单调队列

1、单调队列的概念与实现方式

单调队列在实现上不能依靠STL中的queue,需要手写queue或者用deque双向队列。因为在单调队列中我们对队首与队尾都需要操作。

在概念与实现上,单调栈如果加上一个能从栈顶pop出元素的功能,就是单调队列了,因此理解了单调栈之后再来理解单调队列就不是什么难事了。

通过单调队列,我们可以求出区间最值。

2、单调队列代码实现

相比于单调栈,单调队列的代码实现上会更难一些,原因就是需要手写队列。因此我们需要对栈与队列的原理有比较深的理解。

例题链接:Acwing 滑动窗口

给定一个大小为 n≤1e6 的数组。有一个大小为 k 的滑动窗口,它从数组的最左边移动到最右边。你只能在窗口中看到 k 个数字。每次滑动窗口向右移动一个位置。以下是一个例子:该数组为 [1 3 -1 -3 5 3 6 7],k 为 3。窗口位置                  最小值 最大值
[1  3  -1] -3  5  3  6  7           -1      31 [3  -1  -3] 5  3  6  7           -3      31  3 [-1  -3  5] 3  6  7           -3      51  3  -1 [-3  5  3] 6  7           -3      51  3  -1  -3 [5  3  6] 7            3      61  3  -1  -3  5 [3  6  7]           3      7
你的任务是确定滑动窗口位于每个位置时,窗口中的最大值和最小值。输入格式
输入包含两行。第一行包含两个整数 n 和 k,分别代表数组长度和滑动窗口的长度。第二行有 n 个整数,代表数组的具体数值。同行数据之间用空格隔开。输出格式
输出包含两个。第一行输出,从左至右,每个位置滑动窗口中的最小值。第二行输出,从左至右,每个位置滑动窗口中的最大值。输入样例:
8 3
1 3 -1 -3 5 3 6 7
输出样例:
-1 -3 -3 -3 3 3
3 3 5 5 6 7

分析:单调队列的入队与保持单调的方式是和单调栈一样的,只是代码实现不一样。然后每一次的队首就是答案。由于需要求出最大值和最小值,我们需要写两个for循环来实现单调递增队列和递减队列。

正确代码
注意:在代码中队列数组q存的是数的编号而不是数本身!

//#pragma GCC optimize(2)
#include<iostream>
#include<iomanip>
#include<cstdio>
#include<string>
#include<algorithm>
#include<cmath>
#include<queue>
#include<vector>
#include<map>
#include<stack>
#include<set>
#include<bitset>
#include<ctime>
#include<cstring>
#include<list>
#define ll long long
#define ull unsigned long long
#define INF 0x3f3f3f3f
#define mem(a,b) memset(a,b,sizeof(a))
using namespace std;
typedef  pair<int, int> PII;
const int N = 1e6 + 7;int h, t=-1;//队首与队尾指针
int a[N], q[N];//原数组,编号队列void solve()
{int n, k;cin >> n >> k;for (int i = 0; i < n; i++) cin >> a[i];  //输入原数组for (int i = 0; i < n; i++){if (h <= t && i - q[h] + 1 > k)h++;  //如果队列元素数量超过了k,队首pop出队while (h <= t && a[q[t]] >= a[i]) t--;  //保证队列内元素从小到大,同单调栈一样从队尾删除q[++t] = i;  //编号入队if (i + 1 >= k)cout << a[q[h]] << ' ';}cout << endl;h = 0,t = -1;  //初始化for (int i = 0; i < n; i++){if (h <= t && i - q[h] + 1 > k)h++;  //队列元素数量超过了kwhile (h <= t && a[q[t]] <= a[i]) t--;  //保证队列内元素从大到小q[++t] = i;  //编号入队if (i + 1 >= k)cout << a[q[h]] << ' ';}}int main()
{std::ios::sync_with_stdio(false);cin.tie(0), cout.tie(0);solve();return 0;
}

如果对代码无法理解的话,试着在纸上画画图吧。

作者:Avalon Demerzel,喜欢我的博客就点个赞吧,更多图论与数据结构知识点请见作者专栏《图论与数据结构》

【数据结构】图文例题详解单调栈与单调队列相关推荐

  1. 【数据结构】单调栈和单调队列 详解+例题剖析

    算法:单调栈和单调队列 一.单调栈和单调队列 二.单调栈例题 1.模板题入门 2.不懂不要急,看这道题 三.单调队列例题 1.入门 2.进阶 一.单调栈和单调队列 单调栈和单调队列与普通的栈,队列不同 ...

  2. 数据结构与算法详解目录

    数据结构与算法详解是一本以实例和实践为主的图书,主要是经典的数据结构与常见算法案例,来自历年考研.软考等考题,有算法思路和完整的代码,最后提供了C语言调试技术的方法. 后续配套微课视频. 第0章  基 ...

  3. 数据结构--图(Graph)详解(四)

    数据结构–图(Graph)详解(四) 文章目录 数据结构--图(Graph)详解(四) 一.图中几个NB的算法 1.普里姆算法(Prim算法)求最小生成树 2.克鲁斯卡尔算法(Kruskal算法)求最 ...

  4. 数据结构--图(Graph)详解(一)

    数据结构–图(Graph)详解(一) 文章目录 数据结构--图(Graph)详解(一) 一.图的基本概念 1.图的分类 2.弧头和弧尾 3.入度和出度 4.(V1,V2) 和 < V1,V2 & ...

  5. 算术编码例题详解_百分数与百分点区别详解

    通过以往的答疑经验,许多同学对于出现百分点的材料和题目不能够很好的百分点与已知百分数之间的关系.那么百分数与百分点的核心区别在哪里?常见题型中出现的百分数与百分点应该如何进行理解应用? 百分数与百分点 ...

  6. 数据结构--图(Graph)详解(三)

    数据结构–图(Graph)详解(三) 文章目录 数据结构--图(Graph)详解(三) 一.深度优先生成树和广度优先生成树 1.铺垫 2.非连通图的生成森林 3.深度优先生成森林 4.广度优先生成森林 ...

  7. 数据结构--图(Graph)详解(二)

    数据结构–图(Graph)详解(二) 文章目录 数据结构--图(Graph)详解(二) 一.图的存储结构 1.图的顺序存储法 2.图的邻接表存储法 3.图的十字链表存储法 4.图的邻接多重表存储法 二 ...

  8. 用数据结构c语言写成绩排序,C语言数据结构 快速排序实例详解

    C语言数据结构 快速排序实例详解 一.快速排序简介 快速排序采用分治的思想,第一趟先将一串数字分为两部分,第一部分的数值都比第二部分要小,然后按照这种方法,依次对两边的数据进行排序. 二.代码实现 # ...

  9. dijkstra标号法表格_标号法求最短路径例题详解.ppt

    标号法求最短路径例题详解 r * 最短路径 带权图G=, 其中w:E?R. ?e?E, w(e)称作e的权. e=(vi,vj), 记w(e)=wij . 若vi,vj不 相邻, 记wij =?. 设 ...

  10. 2013汇总计算 广联达gcl_广联达图形算量GCL2013整体操作流程图文教程详解

    算量 GCL2013 整体操作流程图文教程详解 当您对 GCL2013 软件的整体操作流程不熟悉或不清楚时, 您可以看 看这个简单操作流程. 操作步骤 [第一步]:启动软件: 通过鼠标左键单击 win ...

最新文章

  1. 一次 QPS 翻倍的 Java 服务性能优化
  2. virtuoso从电路图导入版图_基于Virtuoso 平台的单片射频收发系统电路仿真与版图设计...
  3. 阿里巴巴副总裁陈丽娟:我对阿里云产品生态的思考 | 云原生加速器观点
  4. JZOJ 5405. 【NOIP2017提高A组模拟10.10】Permutation
  5. 互联网1分钟 | 0327 华为P30系列发布;微信公号直播工具大范围开放内测资格
  6. IP Cam须改原厂密码防黑客
  7. 计算机基础-软件梗概
  8. 【C语言】通过原子操作实现加减乘除操作Ⅱ
  9. 对于Force.com平台的一些批评 - 持续更新中
  10. 无法进入页面,且浏览器调试界面->Timing报CAUTION:request is not finished yet!
  11. (一)音视频:解码H264文件流程 渲染和拿到解码后源数据YUV 完整Demo
  12. Linux 部署turnserver
  13. html制作一个视频播放器,H5 打造属于自己的视频播放器(HTML 篇)
  14. [UOJ30]/[CF487E]Tourists
  15. Latex系列2---段落编写+标题编写+目录生成
  16. 2D-Driven 3D Object Detection in RGB-D Images
  17. 粗识 HTML5 video 标签和MSE媒体源扩展
  18. 计算机考研专用邮件模板!复试/调剂联系导师邮件怎么写?
  19. 产品设计:《室内设计》
  20. DCDC电源干扰13.56M射频问题调研

热门文章

  1. 前端:用css打造炫酷3d特效- css3d立方体
  2. AJAX初始化combox 并取值
  3. 使用ASP.NET MVC构建HTML5离线web应用程序
  4. JQuery AJAX处理页面返回的XML
  5. mybatis 实现查询商品列表的分页
  6. matlab-自控原理 已知x~=Ax+Bu中的AB矩阵和X0,求单位输入下的时间响应
  7. VMWARE 之 vSphere vCenter 安装基本配置
  8. 2013B题碎纸片拼接
  9. JavaScript设计模式与实践--工厂模式
  10. Java知识积累——参数个数可变的函数(Varargs)