一篇文章彻底弄懂单调栈!!!

news2024/10/5 18:29:54

前言

        最近梳理完中间件后荔枝一边学项目一边刷算法,一刷了代码随想录中的字符串、双指针、栈和队列以及单调栈。其中感觉比较有难度的还是单调栈嘿,因此有必要(水)梳理一篇文章来复盘一下单调栈的相关知识~ 希望复盘完后可以有所收获!


文章目录

前言

一、单调栈

二、经典例题

2.1 Leecode739. 每日温度

2.2 Leecode42. 接雨水

2.3 Leecode84. 柱形图中最大的矩形

总结


一、单调栈

        相信大家跟荔枝一样都了解过栈这种数据结构吧嘿嘿嘿,栈是一种先进后出的数据结构,在C++中可以使用stack容器适配器来自行选择这种数据结构的底层实现,比如list、vector、deque等。STL默认是采用双端队列Deque来匹配栈的底层的,这里就不过多赘述了。单调栈,顾名思义就是一个单调的栈,它其实就是栈这种数据结构的变种,栈中的元素或按照从小到大顺序排序、或者按照从大到小的顺序排序。也就是说,默认情况下有两种单调栈:单调递增的栈、单调递减的栈。

应用场景:

单调栈一般用在这样的场景:在一组元素nums中,找到任意元素右边第一个最大|小的元素。当然也可以是拿来找左边的。 

可能有些小伙伴可能会说:直接暴力出来不就行了,但是直接暴力还是有可能超时滴,单调栈的时间复杂度是O(n)噢。

跟着代码随想录顺序刷,第一道单调栈的题目是Leecode739. 每日温度,这道题目是单调栈系列的模板题,


二、经典例题

2.1 Leecode739. 每日温度

题目描述:

        给定一个整数数组 temperatures ,表示每天的温度,返回一个数组 answer ,其中 answer[i] 是指对于第 i 天,下一个更高温度出现在几天后。如果气温在这之后都不会升高,请在该位置用 0 来代替。

输入样例:

temperatures = [73,74,75,71,69,72,76,73]

输出样例:

[1,1,4,2,1,1,0,0]

        看完题目,那么我们要选择单调递增的单调栈还是单调递减的单调栈来模拟呢?荔枝觉得还是需要自己手动模拟一下,模拟一遍大概整个实现过程也就了解了。首先荔枝来手动模拟递增的单调栈。

递增单调栈

我们给出一组数据来模拟单调栈解题地过程 

        首先我们需要定义一个栈来维护记录每一个元素在原容器中的下标,一个vector容器来记录我们需要的结果。在遍历开始之前需要先往stack中push原vector容器template中的第一个元素,这是为了方便后面的比较逻辑。for循环的遍历将从1开始,为了保证栈是单调递增的,如果当前遍历的元素大于stack栈顶元素下标所对应的元素,我们需要将栈顶元素弹出,不过在弹出之前我们可以发现,对于栈顶元素我们要求的最近的最大值其实就是我们当前遍历的元素,根据题目要求的是距离最近的更高温度,那我们就需要实时更新我们的result,因此就有result [stack.top()] = i-stack.top();当前遍历的元素小于或者等于stack栈顶元素下标所对应的元素时,符合单调递增的要求,因此直接push()就可以咯。所以模拟一遍之后我们发现,使用单调递增的单调栈可以用来寻找数组各个元素的右边最近更大元素!

class Solution {
public:
    //单调栈来实现
    vector<int> dailyTemperatures(vector<int>& temperatures) {
        stack<int> stack;
        vector<int> result(temperatures.size(),0);
        //先把头元素加进去
        stack.push(0);
        for(int i=1;i<temperatures.size();i++){
            if(temperatures[i]==temperatures[stack.top()]){
                stack.push(i);
            }else if(temperatures[i]<temperatures[stack.top()]){
                stack.push(i);
            }else{
                while(!stack.empty() && temperatures[i]>temperatures[stack.top()]){
                    result[stack.top()] = i-stack.top();
                    stack.pop();
                }
                stack.push(i);
            }
        }
        return result;
    }
};

递减单调栈

        看完递增单调栈的模拟,我们继续试着模拟一下递减单调栈的过程。同样为了保证栈是单调递减的,一次对于每一个遍历的元素i,如果template[i]大于或等于栈顶元素所对应的值,就直接push进栈中即可;如果小于栈顶元素所对应的值,我们需要将栈顶元素弹出,在弹出之前,我们依旧比较容易就可以得出:当前遍历的元素其实就是栈中压着的大于该元素的最近右边最小值!还是比较好理解滴,因为栈中是单调递减的,栈中的某个元素a如果比现在遍历的元素b小,那么在while循环执行完后自然会将a压入b的上方。因此回到上面的题目中,我们得到的result其实是求每个天气的下一个更低温度。

demo示例

class Solution {
public:
    //单调栈来实现
    vector<int> dailyTemperatures(vector<int>& temperatures) {
        stack<int> stack;
        vector<int> result(temperatures.size(),0);
        //先把头元素加进去
        stack.push(0);
        for(int i=1;i<temperatures.size();i++){
            if(temperatures[i]==temperatures[stack.top()]){
                stack.push(i);
            }else if(temperatures[i]>temperatures[stack.top()]){
                stack.push(i);
            }else{
                while(!stack.empty() && temperatures[i]<temperatures[stack.top()]){
                    result[stack.top()] = i-stack.top();
                    stack.pop();
                }
                stack.push(i);
            }
        }
        return result;
    }
};

2.2 Leecode42. 接雨水

题目描述:

给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。

输入:
height = [0,1,0,2,1,0,1,3,2,1,2,1]

输出
6

        要想计算能够接到的雨水的数量,我们必须求出图中蓝色区域对应的长和宽。对于当前遍历的元素,我们以其为依据,分别求出左边和右边的最近更高值,这就回到了上一个问题的场景 —— 每日温度的求解。我们需要注意的是这里对于左边的最近更高值的处理:由于求右边的最近更高值采用的是递增的单调栈,当前遍历的元素如果比栈顶下标所对应的元素更高,那么栈顶下面的元素就可能是我们要求的下一个左边最近更高值。因此我们需要保存栈顶元素并弹出该元素,这样才能得到栈顶下面的元素。

代码示例:

class Solution {
public:
    int trap(vector<int>& height) {
        stack<int> sta;
        sta.push(0);
        int sum = 0;
        for(int i=1;i<height.size();i++){
            if(height[i] < height[sta.top()]){
                sta.push(i);
            }else if(height[i] == height[sta.top()]){
                sta.push(i);
            }else{
                while(!sta.empty() && height[i]>height[sta.top()]){
                    int m = sta.top();
                    sta.pop();
                    if(!sta.empty()){
                        int h = min(height[i],height[sta.top()]) - height[m];
                        int w = i-sta.top()-1;
                        sum += h*w;
                    }
                }
                sta.push(i);
            }
        }
        return sum;

    }
};

 demo中其实相对于每日温度改变的其实也比较少哈哈~

2.3 Leecode84. 柱形图中最大的矩形

题目描述:
给定 n 个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1 。

求在该柱状图中,能够勾勒出来的矩形的最大面积。

输入:

heights = [2,1,5,6,2,3]


输出:

10

        求柱形图中最大矩形其实是一道跟接雨水问题相似的题目,只不过求的是凸起的最大面积,因此我们中间的元素一定是比两侧的大,所以使用的一定是递减的单调栈。区别于接雨水问题,这道题目是以栈顶元素为基准去找左右两边的第一个小于它的数left,right,right-left-1的值就是以该基准轴的高度为主的矩形区域的宽度。而高度一定就是我们现在的基准——栈顶元素,因此这里我们也需要一个变量来记录栈顶元素的大小以及在原数组的下标。

demo示例:

class Solution {
public:
    int largestRectangleArea(vector<int>& heights) {
        //这里需要在前后加上0避免在单调递减栈中阻塞的情况
        stack<int> sta;
        int result = 0;
        heights.insert(heights.begin(),0);
        heights.push_back(0);
        sta.push(0);
        //本题目采用的是单调递减栈
        for(int i=1;i<heights.size();i++){
            if(heights[i]>heights[sta.top()]){
                sta.push(i);
            }else if(heights[i] == heights[sta.top()]){
                sta.push(i);
            }else{  
                while(!sta.empty() && heights[i] < heights[sta.top()]){
                    int mid = sta.top();
                    sta.pop();
                    if(!sta.empty()){
                        int left = sta.top();
                        int right = i;
                        int h = heights[mid];
                        int w = right-left-1;
                        result = max(result,w*h);
                    }
                }
                sta.push(i);
            }
        }
        return result;
    }
};

总结

        在上面荔枝给出了两种类型的单调栈的模拟推导,同样也给出荔枝自己的理解思考以及相应的例题对比。其中每日温度无疑是需要仔细打磨的,接雨水问题和求最大矩形分别是对递增单调栈和递减单调栈的应用,其实最基础的模板仍然还是每日温度的题解模板哈哈哈哈。希望荔枝的梳理对小伙伴们有帮助吧哈哈哈哈~~~

今朝已然成为过去,明日依然向往未来!我是荔枝,在技术成长之路上与您相伴~~~

如果博文对您有帮助的话,可以给荔枝一键三连嘿,您的支持和鼓励是荔枝最大的动力!

如果博文内容有误,也欢迎各位大佬在下方评论区批评指正!!!

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

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

相关文章

某音网页端 X-Bogus 参数

逆向目标 目标&#xff1a;某音网页端用户信息接口 X-Bogus 参数 接口&#xff1a;aHR0cHM6Ly93d3cuZG91eWluLmNvbS9hd2VtZS92MS93ZWIvdXNlci9wcm9maWxlL290aGVyLw 什么是 JSVMP&#xff1f; JSVMP 全称 Virtual Machine based code Protection for JavaScript&#xff0c;即 …

根据条件关闭软件

使用下载工具时&#xff0c;经常出现磁盘空间已满&#xff0c;无法下载的情况。 使用shell写一个监控&#xff0c;每2分钟执行一次。判断当前磁盘的空间&#xff0c;低于2G时&#xff0c;关闭下载软件。 获取空间大小 ➜ ~ df -h …

如何运用API接口获取淘宝1688京东商品数据:从入门到实践

一、引言 随着电子商务的飞速发展&#xff0c;许多电商平台提供了API接口&#xff0c;允许开发者获取商品数据&#xff0c;以创建各种创新的应用。本文将详细介绍如何使用API接口获取商品数据&#xff0c;并通过代码示例进行演示。 二、API接口概述 1.API接口定义 API&…

【校招VIP】数据库理论之数据库范式

考点介绍&#xff1a; 范式是指关系数据库中的一种数据结构设计规范&#xff0c;用于规范关系型数据库中数据的存储方式&#xff0c;目的是为了消除冗余数据&#xff0c;减少数据的重复性&#xff0c;提高数据的一致性、完整性和正确性&#xff0c;避免数据的不一致性和冲突 …

三相组合式过电压保护器试验

三相组合式过电压保护器试验 试验目的 三相组合式过电压保护器主要分为有带串联间隙过压保护器和无间隙过压保护器两大类&#xff0c;其试验项目内容要求分别使用高压工频交流和高压直流电源。 三相组合式过电压保护器试验&#xff0c;主要是为了及早发现设备内部绝缘受潮及…

华为云云耀云服务器L实例评测|云耀云服务器L实例部署Dashdot服务器仪表盘

华为云云耀云服务器L实例评测&#xff5c;云耀云服务器L实例部署Dashdot服务器仪表盘 一、云耀云服务器L实例介绍二、Dashdot介绍2.1 Dashdot简介2.2 开发环境要求2.3 Yarn介绍 三、本次实践介绍3.1 本次实践简介3.2 本次环境规划 四、检查服务器环境4.1 购买云耀云服务器L实例…

Android中的缓存策略:LruCache和DiskLruCache

Android中的缓存策略&#xff1a;LruCache和DiskLruCache 导言 本篇文章主要是介绍Android中内置的两个缓存类的原理。所谓缓存&#xff0c;就是将获取的数据保存下来以便下次继续使用&#xff0c;这种技术尤其在网络请求和图片加载中有用&#xff0c;可以显著地提升App的性能…

SSM - Springboot - MyBatis-Plus 全栈体系(十三)

第三章 MyBatis 一、MyBatis 简介 1. 简介 MyBatis 最初是 Apache 的一个开源项目 iBatis, 2010 年 6 月这个项目由 Apache Software Foundation 迁移到了 Google Code。随着开发团队转投 Google Code 旗下&#xff0c; iBatis3.x 正式更名为 MyBatis。代码于 2013 年 11 月迁…

大模型训练显存优化推理加速方案

当前的深度学习框架大都采用的都是fp32来进行权重参数的存储&#xff0c;比如Python float的类型为双精度浮点数fp64&#xff0c;pytorch Tensor的默认类型为单精度浮点数fp32。随着模型越来越大&#xff0c;加速训练模型的需求就产生了。在深度学习模型中使用fp32主要存在几个…

R语言贝叶斯MCMC:GLM逻辑回归、Rstan线性回归、Metropolis Hastings与Gibbs采样算法实例...

原文链接&#xff1a;http://tecdat.cn/?p23236 在频率学派中&#xff0c;观察样本是随机的&#xff0c;而参数是固定的、未知的数量&#xff08;点击文末“阅读原文”获取完整代码数据&#xff09;。 相关视频 什么是频率学派&#xff1f; 概率被解释为一个随机过程的许多观测…

Spark SQL【电商购买数据分析】

Spark 数据分析 &#xff08;Scala&#xff09; import org.apache.spark.rdd.RDD import org.apache.spark.sql.{DataFrame, SparkSession} import org.apache.spark.{SparkConf, SparkContext}import java.io.{File, PrintWriter}object Taobao {case class Info(userId: Lo…

最该考的高含金量计算机证书盘点(文末领资料)

谈到大学规划&#xff0c;不少过来人都会建议萌新们在课余时间多多考证&#xff0c;俗话说的好“证多不压身”&#xff0c;今天我们就来聊一聊&#xff0c;计算机相关专业的大学生&#xff0c;有哪些证书可以考&#xff1f; 首先&#xff0c;不得不提的就是全国计算机二级考试…

web:[ACTF2020 新生赛]Exec

背景知识 命令执行漏洞 linux命令 题目 打开题目&#xff0c;页面显示的是一个ping 尝试一下 查看源代码发现 尝试ping一下百度 由题目名可知这道题关于exec&#xff08;命令执行&#xff09;&#xff0c;这里需要联想到可以多条命令执行 输入baidu.com;ls 尝试;号是否能够…

从统计语言模型到预训练语言模型---预训练语言模型(Transformer)

预训练模型的概念在计算机视觉领域并不陌生&#xff0c; 通常我们可以在大规模图像数据集上预先训练出一个通用 模型&#xff0c; 之后再迁移到类似的具体任务上去&#xff0c; 这样在减少对图像样本需求的同时&#xff0c; 也加速了模型的开发速度。计 算机视觉领域采用 Image…

互联网医院系统|互联网医院软件功能与广阔应用领域

随着科技的不断进步和人们对健康需求的提高&#xff0c;互联网医院已经成为当今医疗领域的热点话题。作为一种融合了互联网和医疗服务的创新模式&#xff0c;互联网医院带来了许多便利和改变。本文将详细介绍互联网医院的软件功能、应用范围以及未来的发展趋势。 互联网医院通过…

【计算机毕业设计】基于SpringBoot+Vue电影在线订票系统的开发与实现

博主主页&#xff1a;一季春秋博主简介&#xff1a;专注Java技术领域和毕业设计项目实战、Java、微信小程序、安卓等技术开发&#xff0c;远程调试部署、代码讲解、文档指导、ppt制作等技术指导。主要内容&#xff1a;毕业设计(Java项目、小程序等)、简历模板、学习资料、面试题…

机器学习笔记:概念对比——损失函数,代价函数,目标函数

损失函数 Loss Function 通常是针对单个训练样本而言 给定一个模型输出 和一个真实值y &#xff0c;损失函数是 代价函数 Cost Function 通常是针对整个训练集&#xff08;或者在使用 mini-batch gradient descent 时一个 mini-batch&#xff09;的总损失 目标函数 Objec…

备考cisp拿证,收藏这一篇就够了

为什么要考CISP 认证机构&#xff1a;中国信息安全测评中心&#xff0c;是中央批准成立的国家权威信息安全测评机构&#xff0c;CISP是当之无愧的国家级认证&#xff0c;是国内对信息安全从业人员资质能力的最高认可。 持证人数&#xff1a;在信息安全行业&#xff0c;持有CI…

多维数据可视化技术,Radviz可视化原理,向量化的 Radviz(vectorized Radviz,简称 VRV)

目录 多维数据可视化技术 Radviz可视化原理 向量化的 Radviz(vectorized Radviz,简称 VRV) 多维数据可视化技术 多维和高维数据普遍存在于我们的日常生活和科学研究中 . 比如 , 手机就包括品牌、型号、尺寸、重量、 生产日期、屏幕尺寸和电池容量等几十个属性; 又如 , 生物…

Pygame中Sprite类的使用3

在Pygame中Sprite类的使用2_棉猴的博客-CSDN博客中提到了通过派生自pygame.sprite.Sprite类的自定义类Zombie&#xff0c;可以实现一个僵尸的移动。可以通过pygame.sprite.Group类实现对多个Zombie类实例的管理&#xff0c;即可以实现多个僵尸的移动。 1 pygame.sprite.Group类…