第九章:单调栈与单调队列

news2024/11/17 5:53:31

单调栈与单调队列

  • 一、单调栈
    • 1、什么是单调栈?
    • 2、单调栈的模板
      • (1)问题:
      • (2)分析:
  • 二、单调队列
    • 1、什么是单调队列
    • 2、单调队列模板
      • (1)问题
      • (2)分析

一、单调栈

1、什么是单调栈?

单调栈顾名思义就是栈中的元素是具有单调性的,比如依次增大的数则具有单调递增的性质,依次减小的数则具备单调递减的性质。

2、单调栈的模板

(1)问题:

在这里插入图片描述

(2)分析:

我们可以写出一个暴力做法:如下:

#include<iostream>
using namespace std;
const int N=100010;
int a[N];
int main()
{
    int n;
    cin>>n;
    for(int i=0;i<n;i++)
    {
        scanf("%d",a+i);
    }
    for(int i=0;i<n;i++)
    {
        int j,flag=0;
        for(j=i-1;j>=0;j--)
        {
            if(a[j]<a[i])
            {
                flag=1;
                break;
            }
        }
        if(flag)cout<<a[j]<<" ";
        else cout<<-1<<" ";
    }
    return 0;
}

但是这种暴力做法的时间复杂度是O(N2),因此当我们提交的时候会发生超时的情况。因此,我们需要寻找另外的算法来优化时间复杂度。

此时,我们可以采用空间换时间的思想,具体思路如下:

我们除了创建一个数组去存储输入的数据外,我们再开辟一块空间来处理数据。题目中提到的是左端的第一个,此时我们很容易就联想到了栈这种数据结构,因为这种数据结构是先进后出,假设我们再存储到数组中的同时,也存储到栈中,那么我们的栈顶元素一定是前距离a[i]左端最近的数字。

因此,当我们使用栈的时候,就能够保证我们输出的栈顶元素是距离目标元素最近的,那么我们接下来的问题就是如何保证,我们输出的栈顶元素一定是比目标元素小呢?

我们看下面的思路:

  • step1 : 在栈不为空的条件下,只要栈顶元素大于等于栈顶元素,那么我们就删除掉栈顶元素。
  • step2 : 让目标元素进栈。

然后我们来分析一下上面的思路:
步骤一就保证了栈中的任意一个元素,一定是小于后一个元素的。所以此时栈中的元素呈现了单调递增的特性。

接着我相信大家会在两个点上感到疑惑:

1、删除的元素会不会是后续过程中的答案?

我们看下面的这种情况下,5是否有可能成为后续过程的答案?
假设我们的目标元素是6,那么5小于6,但是由于5>3,根据不等式的传递性,3也小于6,但是3距离元素6更近,所以3一定是答案。那么这是数字3存在的情况。我们假设数字3被删除了,我们假设3遇到了2,此时3大于等于2,所以3会被删除,但是此时入栈的元素是不是2,很明显再2的存在下,5更不可能成为答案。综上所述,我们删除的元素是不可能成为答案的。
在这里插入图片描述
2、目标元素一定要入栈吗?

答案是一定的,因为通过步骤一的循环处理,栈具备单调递减的特点,因此我们的目标元素就会成为栈中最小的元素,同时这个元素还是距离下一个元素最近的。假设这个元素都大于目标元素,那么剩余元素必定大于目标元素,所以这个栈中就不会再存在答案了。综上所述,我们的目标元素是最有可能成为下一次判断时的答案。

因此,我们的思路一定能够输出正确的答案,那么我们如何实现呢?

#include<iostream>
using namespace std;
const int N=100010;
int a[N],stk[N],top;
int main()
{
    int n;
    scanf("%d",&n);
    for(int i=0;i<n;i++)
    {
        scanf("%d",a+i);
        while(stk&&stk[top]>=a[i])top--;
        if(!top)cout<<-1<<" ";
        else cout<<stk[top]<<" ";
        stk[++top]=a[i];
    }
    return 0;
}

我们发现上面的时间复杂度是O(N),大大的优化了时间。

二、单调队列

1、什么是单调队列

单调队列就是队列中的元素具有单调性的队列。

2、单调队列模板

(1)问题

在这里插入图片描述

(2)分析

我们以最小值为例:

这道题我们同样可以采用暴力的做法:两个for循环相互嵌套。我们发现这种情况的时间复杂度是O(N2)。很明显,这种算法是非常低效的。

那么我们换一种算法:

我们先来思考一下,这道题适合什么样的数据结构?不难发现,这个滑动窗口是从前向后移动,即先进先出。所以我们想到了队列这种数据结构来存储。

那么队列是在队头输出的,因此我们需要保证队头输出的数据是最小值。此时受到刚才单调栈的影响,我们很容易想到这里应该是构造一个单调递增的队列,这样我们就能够保证队列中的第一个元素是最小的。

此时大家就会有一个疑问,为什么必须单调呢?我们明明只需要保证第一个最小即可。那么我们假设第一个是最小的,第二个是最大的。当我们的第一个元素滑出窗口的时候,第二个元素成为了第一个元素,此时第一个元素就不再是最小的。因此,我们保证单调性的时候,就能够保证每个元素做队头的时候,都小于后续的元素。

我们如何实现呢?单调递增的特点就是前一个元素小于该元素。接着我们就能写出下列的步骤:

  • step1 : 在队列不为空的条件下,判断第一个元素是否在窗口内。
  • step2 : 若队尾元素大于等于目标元素则删除
  • step3 :目标元素进队。

在进行步骤1的判断的时候,我们需要一个计数器去记录,但是这样是相当麻烦的,因此我们将下标存储到队列中,这样的话,我们就能够通过下标的方式去判断队头是否出队。

我们还是解决一下几个问题:
为什么要不满足单调性的元素可以删除?
在这里插入图片描述

在这个队列中,只要有3的存在,4必不可能是答案,那么当3不存在的时候呢?那么4更不可能是答案了,因为我们是在队头删除元素的。所以3出队的前提是4出队。因此只有4没有3的情况是不存在的。所以我们可以删除。

那么为什么目标元素一定要进队呢?
因为经过单调性的处理,目标元素就成了队中最大的,虽然目标元素队前的元素比其要小,但是他们都是先出队的,所以当前面的元素都出队的时候,该目标元素有可能成为答案,所以我们要让每个目标元素的下标都进队。

#include<iostream>
using namespace std;
const int N=1000010;
int a[N],q[N],h,t;
int main()
{
    int n,k;
    cin>>n>>k;
    for(int i=0;i<n;i++)
    {
        scanf("%d",a+i);
        if(h<=t&&q[h]<i-k+1)h++;
        while(h<=t&&a[q[t]]>=a[i])t--;
        q[++t]=i;
        if(i-k+1>=0)
        printf("%d ",a[q[h]]);
    }
    puts("");
    h=0,t=-1;
    for(int i=0;i<n;i++)
    {
        if(h<=t&&q[h]<i-k+1)h++;
        while(h<=t&&a[q[t]]<=a[i])t--;
        q[++t]=i;
        if(i-k+1>=0)
        printf("%d ",a[q[h]]);
    }
    return 0;
}

我们发现,判断单调性的时候,我们是在队尾删除,判断是否在窗口内的时候,是在队头删除的。所以这不是普通的队列,而是双端队列,即STL中的deque。所以我们可以通过deque容器来实现。

#include<iostream>
#include<deque>
#include<vector>
using namespace std;
int main()
{
    int n,k,b;
    cin>>n>>k;
    vector<int>a;
    deque<int>q;
    for(int i=0;i<n;i++)
    {
        scanf("%d",&b);
        a.push_back(b);
        if(!q.empty()&&q.front()<i-k+1)q.pop_front();
        while(!q.empty()&&a[q.back()]>=a[i])q.pop_back();
        q.push_back(i);
        if(i-k+1>=0)printf("%d ",a[q.front()]);
    }
    puts("");
    q.clear();
    for(int i=0;i<n;i++)
    {
        if(!q.empty()&&q.front()<i-k+1)q.pop_front();
        while(!q.empty()&&a[q.back()]<=a[i])q.pop_back();
        q.push_back(i);
        if(i-k+1>=0)printf("%d ",a[q.front()]);
    }
    return 0;
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

深入浅出学习透析Nginx服务器的基本原理和配置指南「Https安全控制篇」

Https反向代理 之前的内容中我们主要针对于一些对安全性要求比较高的站点&#xff0c;可能会使用HTTPS&#xff08;一种使用SSL通信标准的安全HTTP协议&#xff09;&#xff0c;针对于HTTP 协议和SSL标准相信大家都知道了&#xff0c;在这里我就不为大家进行介绍了&#xff0c…

共建“医疗合规科技实验室”,美创科技实力护航医疗数据安全

11月15日-17日&#xff0c;由工业和信息化部、深圳市人民政府主办&#xff0c;中国互联网协会、广东省通信管理局、深圳市工业和信息化局等单位承办的2022中国互联网大会隆重召开。 在互联网医疗健康合规发展论坛上&#xff0c;医疗合规科技实验室合作伙伴计划正式启动&#xf…

scau Java综合性实验之Java源程序分析程序

1. 编写一个Java应用程序&#xff0c;实现对某个目录中的所有Java源程序文件&#xff08;包含该目录的子目录中的源程序文件&#xff09;进行统计。统计内容包括&#xff1a; (1) 目录中每个源程序文件的总行数和空白行数&#xff0c;文件的字节数&#xff1b; (2) 目录中所有源…

ADB调试--详细教程(附华为手机无法显示设备解决方法)

终端打开开发者模式&#xff0c;用数据线连接电脑&#xff0c;然后按照下面的步骤操作 1、开启开发者选项&#xff1a; 设置->关于设备->版本号&#xff08;连续点击5次&#xff09; 2、打开USB调试 在开发者选项中&#xff0c;找到USB调试&#xff0c;将此打开。 3、…

作为资深程序民工怎么能被性能优化难倒!原理与实战齐飞,源自大厂自然更专业!

性能优化是一个很复杂的工作&#xff0c;且充满了不确定性。它不像Java业务代码&#xff0c;可以一次编写到处运行(write once, run anywhere)&#xff0c;往往一些我们可能并不能察觉的变化&#xff0c;就会带来惊喜/惊吓。能够全面的了解并评估我们所负责应用的性能&#xff…

全渠道商城授权管控经销商,渠道商管理系统助力医药企业快速扩大渠道规模

随着医改的稳步推进&#xff0c;医药行业传统的以销售为主的扩张模式难以为继&#xff0c;国内药企面临创新转型。如何探寻医药数字化营销方法论&#xff0c;如何把握政策机遇和用户需求&#xff0c;利用数字化推动医药创新渠道破局&#xff0c;已成业内关注的重点。 后疫情时…

如何在win11中用双硬盘或移动硬盘装Ubuntu 20.04 双系统

首先明确一下思路&#xff0c;这个多硬盘的安装方式与单硬盘的方式没什么本质区别 下面介绍具体的方法&#xff1a; 1.下载Ubuntu系统镜像、制作系统盘 1.1 下载镜像 ubuntu20.04镜像下载&#xff1a;ubuntu20.04官网&#xff0c;点击进入下载 现在最新版是 Ubuntu 22.04.1…

ZX297520V3T:Codec NAU88C22驱动调试

一、音频驱动框架 ALSA(Advanced Linux Sound Architecture)是目前linux的主流音频体系结构。ALSA不仅在内核设备驱动层提供了alsa-driver,同时在应用层为我们提供了alsa-lib,应用程序只要调用alsa-lib提供的API,即可以完成对底层音频硬件的控制。为了方便调试,ALSA也提…

Spring~五种存储Bean对象的类注解、方法注解(@Bean)以及Bean对象的获取

目录 五种存储Bean对象的类注解 ​Controller Service Repository Component Configuration 方法注解Bean 使用Bean注解的常见问题 当一个类型有多个实例对象&#xff0c;使用类型获取就会报错 在容器中找不到Bean&#xff0c;不论通过什么方式来获取Bean对象都会报…

建设一个互联网医院系统要花多少钱?

建设一个互联网医院系统要花多少钱&#xff1f;很多人在筹备调研互联网医院建设后&#xff0c;仍然有很多问题答案不清楚&#xff0c;今天就给大家来聊一聊。 其实互联网医院看似简单&#xff0c;实则是一个较为复杂的过程&#xff0c;要想合法合规的开展线上诊疗业务&#x…

雷电模拟器dnconsole命令汇总

雷电模拟器之文件操作 删除相册中的文件 ldconsole adb --index 0 --command "shell rm /sdcard/DCIM/1.png" 雷电模拟器应用操作 假设启动名为test1的模拟器&#xff0c; 以抖音为例 &#xff1a; 判断应用是否安装&#xff1a; ldconsole adb --name test1 --co…

姿态识别+校准|视觉技术新突破

技术实现路径及优势 概述&#xff1a;通过2D图像序列加2D图像景深序列&#xff0c;利用复杂的3D重建算法与人体骨架模型的拟合还原姿态节点信息&#xff0c;进而对各种姿态进行准确的数学分析&#xff0c;达到准确高效识别姿态的效果&#xff1b;摆脱人脸3D训练样本真值依赖&am…

【输出重定向】Windows下 cmd 、powershell输出重定向

目录一、cmd和powershell二、什么是输入输出重定向三、语法及示例一、cmd和powershell 大家如果只用过Windows&#xff0c;可能cmd接触的相对多一点。按win r 输入cmd即可打开。我一般用的是powershell。 cmd:     cmd是command的缩写&#xff0c;即命令提示符。是提示命…

怎么手写转文字?借助这3款工具轻松实现

许多小伙伴在日常中会遇到需要把手写文字转换为电子版的情况&#xff0c;例如领导给一份手稿让你整理&#xff0c;亦或是平时自己随笔记录的一些内容想把它分享到平台上等等。但是手动逐字敲键盘输出太费时费神啦&#xff0c;所以今天要传授给大家一个好方法&#xff0c;那就是…

HTML5期末考核大作业,网站——青岛民俗 7页。 美丽家乡 学生旅行 游玩 主题住宿网页

&#x1f468;‍&#x1f393;静态网站的编写主要是用 HTML DⅣV CSSJS等来完成页面的排版设计&#x1f469;‍&#x1f393;&#xff0c;一般的网页作业需要融入以下知识点&#xff1a;div布局、浮动定位、高级css、表格、表单及验证、js轮播图、音频视频Fash的应用、uli、下拉…

LeetCode-808-分汤

1、动态规划法 我们可以利用二维数组dp[i][j]dp[i][j]dp[i][j]来记录当汤A的体积为i&#xff0c;汤B的体积为j时的概率。由于体积均为25的倍数&#xff0c;为了方便计算我们可以将所有体积都除以25。由于误差范围为10−510^{-5}10−5&#xff0c;我们可以计算得出当$n\ge179时…

Day08--自定义组件的插槽

1.什么是插槽 **************************************************************************************************************************************************************************************************************************** 2.单个插槽 我的操作&am…

一个小台灯 之 微信小程序开发日志

微信小程序开发日志 小程序只能使用https和后台服务器进行post请求和get请求&#xff0c;使用https的连接需要的微信小程序的后台对域名进行备份。 在备份的时候也只能通过域名&#xff0c;不能通过公网IP备份。 一、基础 微信小程序的一个界面主要分为四个文件 index.json…

剖析一下“抢茅台“脚本底层逻辑

本文作者&#xff1a;梁冬冬 前言&#xff1a; 今天你撸茅台了么&#x1f447;&#xff1f; 撸茅台已经成为社会现象&#x1f64c;&#xff0c;茶余饭后讨论的最佳实践&#x1f607; 2022年双十一大促已经完美收官&#xff0c;兄弟姐妹克服种种困难与挑战… 备战的会议室忙碌…

用户规模持续上涨,外卖行业未来的发展趋势如何?还能从里盈利?

外卖跑腿行业随着互联网移动支付及网络的普及得到了高速发展&#xff0c;相关数据显示2022年外卖行业规模进一步上升至8117亿元&#xff0c;外卖市场发展速度增速加快&#xff0c;并且也带动了各大主流外卖平台的发展&#xff0c;在2021年外卖用户规模达5.44亿人&#xff0c;占…