本系列所有题目均为Acwing课的内容,发表博客既是为了学习总结,加深自己的印象,同时也是为了以后回过头来看时,不会感叹虚度光阴罢了,因此如果出现错误,欢迎大家能够指出错误,我会认真改正的。同时也希望文章能够让你有所收获,与君共勉!

昨天写了一道表达式求值就已经快寄了,今天要写的队列,单调栈,单调队列就比较好写了,还好都是模板。

模拟队列

实现一个队列,队列初始为空,支持四种操作:

push x – 向队尾插入一个数 x;

pop – 从队头弹出一个数;

empty – 判断队列是否为空;

query – 查询队头元素。

现在要对队列进行 M 个操作,其中的每个操作 3 和操作 4 都要输出相应的结果。

输入格式

第一行包含整数 M,表示操作次数。

接下来 M 行,每行包含一个操作命令,操作命令为 push x,pop,empty,query 中的一种。

输出格式

对于每个 empty 和 query 操作都要输出一个查询结果,每个结果占一行。

其中,empty 操作的查询结果为 YES 或 NO,query 操作的查询结果为一个整数,表示队头元素的值。

数据范围

1≤M≤100000,

1≤x≤109,

所有操作保证合法。

输入样例:

10 push 6 empty query pop empty push 3 push 4 pop query push 6

输出样例:

NO 6 YES 4

算法原理

队列就一般只需要四个操作,这里我们使用数组来模拟队列q,定义两个指针headtail

初始化head = 0,tail = -1;

  1. 入队
  1. q[tail++] = x;
  1. 出队
  1. head++;
  1. 判断长度
  1. size = tail - head + 1;
  1. 判断为空
  1. if(tail - head + 1 == 0)

代码实现

  1. #include <iostream>
  2. #include <cstring>
  3. #include <algorithm>
  4. using namespace std;
  5. const int N = 1000010;
  6. int q[N],head,tail;
  7. int n;
  8. int main()
  9. {
  10. cin >> n;
  11. head = 0,tail = -1;
  12. while(n--){
  13. int x;
  14. string s;
  15. cin >> s;
  16. if(s == "push"){
  17. cin >> x;
  18. q[++tail] = x;
  19. }
  20. else if(s == "query"){
  21. cout << q[head] << endl;
  22. }
  23. else if(s == "pop"){
  24. head ++ ;
  25. }
  26. else if(s == "empty"){
  27. if(tail - head + 1 == 0){
  28. cout << "YES" << endl;
  29. }
  30. else{
  31. cout << "NO" << endl;
  32. }
  33. }
  34. }
  35. return 0;
  36. }

看,是吧,是不是很简单啊!


单调栈

给定一个长度为 N 的整数数列,输出每个数左边第一个比它小的数,如果不存在则输出 −1。

输入格式

第一行包含整数 N,表示数列长度。

第二行包含 N 个整数,表示整数数列。

输出格式

共一行,包含 N 个整数,其中第 i 个数表示第 i 个数的左边第一个比它小的数,如果不存在则输出 −1。

数据范围

1≤N≤105

1≤数列中元素≤109

输入样例:

5

3 4 2 7 5

输出样例:

-1 3 -1 2 2

算法原理

或许我们也需要先使用暴力尝试去求解,双重循环,外层用变量i遍历这个数列,内层用ji-1处向数组头方向进行搜索,第一个符合条件的就是我们需要的结果,时间复杂度为\(O(n\times n!)\),时间复杂度再某些情况下有点高,因此我们需要对他进行优化,当然也可以优化,就是使用单调栈实现,下面就来看看单调栈是什么。

顾名思义,单调栈就是存储数值呈现单调递增或单调递减的栈,其主要用法就是求左边或右边的第一个比他大或比他小的元素,过程如下。



当我们需要得到左边第一个比他小的数时,我们应该知道在这个过程中那些对我们是有利的信息,那些是无用的信息,进一步可以发现那些可能是比他小的数,那些一定不会是比他小的数,对于一定不是比它小的数,我们可以不去搜索他,对于比他小的数,我们又该以怎样的一个搜索方式去寻找,那必然是需要速度快的搜索方式吧,因此我们可以使用单调栈取存储过去的那些元素,并再栈里面进行寻找。

  • 因为我们需要比它小的,所以对于比它大的元素我们就要把他从栈中去掉
  • 因为我们需要更快速的寻找,所以我们需要用利用单调性去搜索(而不必一一遍历),且应该是单调递增,这时候我们会发现当我们需要第一个比他小的元素时,由于栈的后进先出原则,从栈顶往栈底寻找的第一个比他小的元素就是所需要的元素。注:不管找得到还是找不到当前元素左边第一个比它小的元素,我们都需要把他加进栈中,知道某一个元素比他小,把他移除栈
  • 同理,找到当前元素左边第一个比它大的元素需要一个单调递减的栈。如果需要右边第一个比他小的元素,我们可以逆序遍历使用单调递增的栈,如果需要右边第一个比它大的元素,依然是逆序遍历使用单调递减的栈。

代码实现

  1. #include <iostream>
  2. #include <cstring>
  3. #include <algorithm>
  4. using namespace std;
  5. const int N = 1000010;
  6. int stk[N],top;
  7. int a[N];
  8. int main()
  9. {
  10. int n;
  11. cin >>n ;
  12. for(int i=0; i < n ; ++i) cin >> a[i];
  13. for(int i=0; i < n ; ++i){
  14. while(top && stk[top] >= a[i]) top--;
  15. if(top){
  16. cout << stk[top] << " " ;
  17. }
  18. else{
  19. cout << "-1 " ;
  20. }
  21. stk[++top] = a[i];
  22. }
  23. return 0;
  24. }

相信各位已经理解了吧!


单调队列

其实也就是滑动窗口。

给定一个大小为 n≤106 的数组。

有一个大小为 k 的滑动窗口,它从数组的最左边移动到最右边。

你只能在窗口中看到 k 个数字。

每次滑动窗口向右移动一个位置。

以下是一个例子:

该数组为 [1 3 -1 -3 5 3 6 7],k 为 3。

你的任务是确定滑动窗口位于每个位置时,窗口中的最大值和最小值。

输入格式

输入包含两行。

第一行包含两个整数 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

算法原理

跟单调栈的原理类似,单调队列就是具有单调递增或单调递减的队列。需要注意的是单调队列里存储的时数组元素的下标

我们先寻找暴力解法,然后寻找在这个过程中那些是我们不需要的元素,然后想办法使用单调队列进行优化,我们把每一个数都会推进队列和窗口里,当窗口里的元素足够时才会进行输出,当我们要输出最小值时,我们需要先形成一个单调递增队列,对于队列里比将要入队的元素要大的那些元素,我么需要将他移出队列(毕竟要最小的,如果比将要入队的元素还要小,那以后无论如何都不会选择他们的),这也保证了队列始终是单调递增的,因此队首所对应的元素一定是最小值。注:每一个将要进队列的元素也都要进入队列的

代码实现

  1. #include <iostream>
  2. #include <cstring>
  3. #include <algorithm>
  4. using namespace std;
  5. const int N = 1000010;
  6. int q[N],head,tail;
  7. int n,k;
  8. int a[N];
  9. int main()
  10. {
  11. cin >> n >> k;
  12. for(int i=0 ;i < n ; ++i) cin >> a[i];
  13. for(int i=0; i < n ; ++i){ // 窗口里的最小值
  14. while(head <= tail && q[head] < i-k+1) ++head; // 队首不在窗口里面,就用队首指针
  15. while(head <= tail && a[q[tail]] >= a[i]) --tail; // 队列队尾元素比入队元素要大或者相等就出队(严格单调是不会有两个相等的元素)
  16. q[++tail] = i;
  17. if(i>=k-1){ // 如果窗口达到最大长度就开始输出最小值
  18. printf("%d ",a[q[head]]);
  19. }
  20. }
  21. cout << endl;
  22. head = 0,tail = -1;
  23. for(int i=0; i < n ; ++i){ // 窗口里的最大值
  24. while(head <= tail && q[head] < i-k+1) ++head;
  25. while( head <= tail && a[q[tail]] <= a[i]) --tail;
  26. q[++tail] = i;
  27. if(i>=k-1) printf("%d ",a[q[head]]);
  28. }
  29. return 0;
  30. }

终于写完啦,太不容易辣!

2022-11-11 Acwing每日一题的更多相关文章

  1. CISP/CISA 每日一题 11

    CISA 每日一题(答) 一个合理建造的数据仓库应当支持下列三种基本的查询格式: 1.向上溯源和向下溯源——向上溯源是对数据进行总计:向下溯源是将数据进行细化: 2.交叉溯源——通过通用属性访问数据仓 ...

  2. 老男孩IT教育-每日一题汇总

    老男孩IT教育-每日一题汇总 第几天 第几周 日期 快速访问链接 第123天 第二十五周 2017年8月25日 出现Swap file….already exists以下错误如何解决? 第122天 2 ...

  3. CISP/CISA 每日一题 五

    CISA 每日一题(答) 信息系统审计师要确认系统变更程序中的: 1.变更需求应有授权.优先排序及跟踪机制: 2.日常工作手册中,明确指出紧急变更程序: 3.变更控制程序应同时为用户及项目开发组认可: ...

  4. [每日一题]ES6中为什么要使用Symbol?

    关注「松宝写代码」,精选好文,每日面试题 加入我们一起学习,day day up 作者:saucxs | songEagle 来源:原创 一.前言 2020.12.23日刚立的flag,每日一题,题目 ...

  5. [每日一题]面试官问:谈谈你对ES6的proxy的理解?

    [每日一题]面试官问:谈谈你对ES6的proxy的理解? 关注「松宝写代码」,精选好文,每日一题 作者:saucxs | songEagle 一.前言 2020.12.23 日刚立的 flag,每日一 ...

  6. 【js】Leetcode每日一题-完成所有工作的最短时间

    [js]Leetcode每日一题-完成所有工作的最短时间 [题目描述] 给你一个整数数组 jobs ,其中 jobs[i] 是完成第 i 项工作要花费的时间. 请你将这些工作分配给 k 位工人.所有工 ...

  7. 【JavaScript】Leetcode每日一题-青蛙过河

    [JavaScript]Leetcode每日一题-青蛙过河 [题目描述] 一只青蛙想要过河. 假定河流被等分为若干个单元格,并且在每一个单元格内都有可能放有一块石子(也有可能没有). 青蛙可以跳上石子 ...

  8. 【JavaScript】Leetcode每日一题-平方数之和

    [JavaScript]Leetcode每日一题-平方数之和 [题目描述] 给定一个非负整数 c ,你要判断是否存在两个整数 a 和 b,使得 a2 + b2 = c . 示例1: 输入:c = 5 ...

  9. 【python】Leetcode每日一题-寻找旋转排序数组中的最小元素

    [python]Leetcode每日一题-寻找旋转排序数组中的最小元素 [题目描述] 已知一个长度为 n 的数组,预先按照升序排列,经由 1 到 n 次 旋转 后,得到输入数组.例如,原数组nums ...

  10. 【JavaScript】【dp】Leetcode每日一题-解码方法

    [JavaScript]Leetcode每日一题-解码方法 [题目描述] 一条包含字母 A-Z 的消息通过以下映射进行了 编码 : 'A' -> 1 'B' -> 2 ... 'Z' -& ...

随机推荐

  1. Neo4j在linux上的安装与Springboot的集成

    Neo4j在linux上的安装与Springboot的集成 在linux安装: 前提:安装配置好java环境 1.下载neo4j 官方社区版下载地址:https://neo4j.com/downloa ...

  2. KingbaseES XA 分布式事务

    在分布式系统中,各个节点(或者事务参与方)之间在物理上相互独立,各节点之间无法确切地知道其它节点中的事务执行情况,所以多节点之间很难保证ACID,尤其是原子性.如果是单节点的事务,由于存在事务机制,可 ...

  3. 【一月一本技术书】-【Go语言设计与实现】- 9月

    Go : 2009.11.10 代表作:Docker.k8s.etcd 模仿C语言,目标:互联网的C语言 讲的晦涩难懂....硬板..放弃了好几次才读完.满分10分,打6分. 下个月:Python数据 ...

  4. kvm安装windows使用virtio驱动

    Windows安装VirtIO驱动的两种方法 已经使用IDE方式来安装好系统 (1)安装完Windows后,创建一块临时的硬盘和网卡,将其驱动都设置为virtio模式添加到Windows中 (2) 添 ...

  5. 第四章:Django表单 - 3:Django表单字段汇总

    Field.clean(value)[source] 虽然表单字段的Field类主要使用在Form类中,但也可以直接实例化它们来使用,以便更好地了解它们是如何工作的.每个Field的实例都有一个cle ...

  6. 2流高手速成记(之三):SpringBoot整合mybatis/mybatis-plus实现数据持久化

    接上回 上一篇我们简单介绍了基于SpringBoot实现简单的Web开发,本节来看Web开发中必不可少的内容--数据持久化 先看项目结构: 1. 创建数据表 打开mysql,打开数据库 test (没 ...

  7. Springboot 之 HandlerMethodReturnValueHandler 运用

    简介 现在项目中大部分采用前后端分离的架构,采用这种架构的项目,在返回数据时,几乎都是采用返回 json 格式的数据.而 spring 中返回 json 格式的数据一般采用 @RestControll ...

  8. 大数据常用的Linux命令

    Linux文件系统基础知识 要想熟练使用命令,就先要熟练掌握Linux文件系统基础知识: 三个路径 当前路径:也叫当前工作目录,就是当前状态下用户所处的位置 相对路径:相对于当前工作目录开始的路径,会 ...

  9. Java连接MySQL数据库。编写一个应用程序,在主类Test_4类中,通过JDBC访问stu数据库,显示t_student表中的内容(表结构见表1),显示效果自己设计。

    题目2:编写一个应用程序,在主类Test_4类中,通过JDBC访问stu数据库,显示t_student表中的内容(表结构见表1),显示效果自己设计.之后,可根据显示的内容进行某条记录的删除(以id为条 ...

  10. P1073 [NOIP2009 提高组] 最优贸易 (最短路spfa)

    本题就是在一条1-n的路径上找p,q(先经过p),使得q-p最大. 考虑建正反图,正图上求出d[x],表示1-x的路径经过的节点最小值,反图上则从n开始求出f[x],x-n的最大值,最后枚举断点i,取 ...