一篇文章带你搞懂 单调栈 是怎么回事

news2024/11/16 10:40:26

首先我们要搞懂什么时候使用单调栈?

当我们需要找到 左边或右边 第一个 比自己大的数 或者 比自己小的数 时就要使用单调栈

单调栈实际上就是一个栈,他的作用就是存储我们遍历过的数字。当我们遍历数组的时候,遍历到后面的数组后并不知道前面遍历过的元素是什么,所以单调栈的作用就是用于存储遍历过的数字。

使用单调栈需要注意的事项

1.确定栈内的元素的单调顺序(重要) 2.确定单调栈内存放的元素

介绍完单调栈的本质以及使用的注意事项以后就来介绍一下怎么用单调栈寻找左边或右边第一个 比自己大的数或者比自己小的数了 

注意我们数组里面存放的是数组下标,便于我们第一个更大或更小的元素距离自身有多远

 

 

了解完怎么使用单调栈以后就来看看常见的单调栈题目了


739. 每日温度

 题意:给定一个整数数组 temperatures ,表示每天的温度,返回一个数组 answer ,其中 answer[i] 是指对于第 i 天,下一个更高温度出现在几天后

那么也就是寻找下一个更小的元素那么我们就使用单调栈

首先明确两点:1.栈内元素是单调递增 (从栈底到栈顶)2.栈内存放的是数组的下标

那么当栈顶元素大于数组元素时

那么直接入栈

            if(num <= temperatures[stack.peek()]){
				stack.push(i);
			}

当栈栈顶元素小于数组元素时

那么不断比较栈顶元素,直到栈顶元素小于该遍历元素

while(!stack.isEmpty() && num > temperatures[stack.peek()]){
    int top = stack.peek();
	result[top] = i - top;
    stack.pop();
}
stack.push(i);

最后要我们返回的是该日究竟在几天后才会出现温度更低的情况,所以我们的返回数组里面存放的是距离

所以才会有这段代码

result[top] = i - top;

 最后贴上完整代码

class Solution {
    public int[] dailyTemperatures(int[] temperatures) {		
    	int len = temperatures.length;
		int[] result = new int[len];
        //创建单调栈
        Stack<Integer> stack = new Stack();
        stack.push(0);
        //遍历温度
        for(int i = 1;i < len;i++){
        	int num = temperatures[i];
            if(num <= temperatures[stack.peek()]){
				stack.push(i);
			}else{
            	while(!stack.isEmpty() && num > temperatures[stack.peek()]){
                	int top = stack.peek();
					result[top] = i - top;
                    stack.pop();
				}
                stack.push(i);
            }
        }
        return result;
    }
}

496. 下一个更大元素 I

寻找更大的元素问题,那么我们还是使用单调栈来解决

这道题有两个数组,一个nums1 一个nums2,题目是想要在 nums2 中找到 nums1 中每个值的下一个更大元素,我这里使用哈希表来建立映射关系,将nums1中的元素和下标都放进哈希表里面

还是在nums2中使用单调栈存遍历过的元素,当发现更大的元素以后就弹出栈顶元素,但是这里跟以往不同的地方在于,我们需要检查栈顶元素是否存在于哈希表中,如果存在那么就在相应的下标处(新建好的数组)存放更大的元素

if(map.containsKey(nums2[stack.peek()])){
		int index = map.get(nums2[stack.peek()]);
        result[index] = nums2[i];
}

总体的思路解决完了,现在就来直接写代码

class Solution {
    public int[] nextGreaterElement(int[] nums1, int[] nums2) {
        int len = nums1.length;
        //创建result数组
        int[] result = new int[len];
        Arrays.fill(result,-1);
    	//创建映射关系
        HashMap<Integer,Integer> map = new HashMap();
        for(int i = 0;i < nums1.length;i++){
			map.put(nums1[i],i);
		}
        //创建单调栈
        Stack<Integer> stack = new Stack();
        stack.push(0);
        
        //遍历数组
        for(int i = 1;i < nums2.length;i++){
			if(nums2[stack.peek()] >= nums2[i]){
				stack.push(i);
			}else{
                while(!stack.isEmpty() && nums2[stack.peek()] < nums2[i]){
					if(map.containsKey(nums2[stack.peek()])){
						int index = map.get(nums2[stack.peek()]);
                        result[index] = nums2[i];
					}
                    stack.pop();
				}
                stack.push(i);
			}
		}
        return result;
    }
}

503. 下一个更大元素 II

还是每日温度的变式,现在我们是按照数组循环来找到更大的元素

面对数组循环问题,我们通常使用将 遍历长度为数组的两倍 来进行解决

然后 数组下标 = 数组下标%数组长度 的方式来解决

这里画了一幅图来解释

关键的难点解决了剩下就简单了 直接上代码

class Solution {
    public int[] nextGreaterElements(int[] nums) {
        int len = nums.length;
        //创建结果数组
        int[] result = new int[len];
        Arrays.fill(result,-1);
        //创建单调栈
        Stack<Integer> stack = new Stack();
        stack.push(0);
        //遍历数组
        for(int i = 1;i < 2*len;i++){
            if(nums[i % len] <= nums[stack.peek()]){
                stack.push(i % len);
            }else{
                while(!stack.isEmpty() && nums[i % len] > nums[stack.peek()]){
                    result[stack.peek()] = nums[i % len];
                    stack.pop();
                }
                stack.push(i % len);
            }
        }
        return result;
    }
}

42. 接雨水

接雨水是最经典的一道面试题了,接下来就来看看怎么解决

以示例1为例子,我们看到从左向右,当遇到比自己更高的柱子就可以接住雨水

那么很明显我们可以转换成单调栈问题,即寻找更大的元素

假如说我们找到了更高的柱子,那么又应该怎么来处理雨水的面积呢?

计算面积是需要3个点的,左中右才能计算出雨水的面积,不理解的同学可以参考下面这张图

右边的柱子是遇到的更大高度,中间的柱子是弹出的栈顶元素,左边的柱子是没有弹出的栈顶元素

我们将 右边柱子的下标 - 左边柱子的下标 - 1 作为雨水的宽度

左右两个柱子的更低高度 - 中间柱子的高度 作为雨水的高度

(为什么是最低还是可以参考上面这张图)

那么雨水的面积就等于 雨水的高度 x 雨水的宽度

同时也要牢记 雨水的面积是一步一步累加加起来的

分析完怎么计算雨水面积以后我们就来分析一下这一块的面积

 

一步一步分析

第一步

这一块面积根据计算我们可以得出接到的雨水面积是 -1

第二步

这一块面积根据计算我们可以得出接到的雨水面积是 2

第三步

这一块面积根据计算我们可以得出接到的雨水面积是 3

那么累加起来我们就可以得到这一块的面积是4了 

具体分析完我们直接上代码

public int trap(int[] height) {
    int len = height.length;
    int ans = 0;
    //创建单调栈
    Stack<Integer> stack = new Stack();
    stack.push(0);
    //遍历数组
    for(int i = 1;i < height.length;i++){
        if(height[i] <= height[stack.peek()]){
            stack.push(i);
        }else{
            while(!stack.isEmpty() && height[i] > height[stack.peek()]){
                //需要三个元素
                int mid = stack.pop();
                if(!stack.isEmpty()){
                    int left = stack.peek();
                    int w = i - left - 1;
                    //计算高度
                    int h = Math.min(height[left],height[i]) - height[mid];
                    int s = h*w;
                    ans += s;
                }
            }
            stack.push(i);
        }
    }

    return ans;
}

84. 柱状图中最大的矩形

同样是寻找最大的面积,但是本题的做法与上一题相反,上一题是寻找更大的元素

但是本题的做法则是寻找更小的元素

为什么?我们可以举例说明

想要获得最大的面积 我们就需要得到最高柱子两边的高度 这与接雨水的解题思路是相反的

接雨水是想要获得最大面积,需要获得最低柱子两边的的高度

那么 获得最高柱子右边的高度,那么就要用到 单调栈 来寻找了(很显然是寻找更小的元素)

虽然解题思想不一样,但是本题的很多细节还是和接雨水相同的

比如处理面积,那么还是一样的做法,计算出宽度的方式还是 right - left  - 1通过左右边柱子计算出宽度

但是高度的处理方式就不一样了,这里是直接用栈顶弹出柱子的高度

那么分析完毕 我们直接上代码

class Solution {
    public int largestRectangleArea(int[] heights) {
        int ans = 0;
        //创建单调栈 单调递减
        Stack<Integer> stack = new Stack();
        stack.push(0);
        //创建新数组
        int len = heights.length;
        int[] newHeight = new int[len + 2];
        newHeight[0] = 0;
        newHeight[len + 1] = 0;
        for(int i = 1;i < len + 1;i++){
            newHeight[i] = heights[i-1];
        }
        //遍历数组
        for(int i = 1;i < newHeight.length;i++){
            //保证单调递减
            if(newHeight[i] >= newHeight[stack.peek()]){
                stack.push(i);
            }else{
                while(!stack.isEmpty() && newHeight[i] < newHeight[stack.peek()]){
                    int mid = stack.pop();
                    if(!stack.isEmpty()){
                        int left = stack.peek();
                        int right = i;
                        //计算宽度
                        int w = right - left - 1;
                        //计算高度
                        int h = newHeight[mid];
                        //计算面积
                        ans = Math.max(h*w,ans);    
                    }
                }
                stack.push(i);
            }
        }
        return ans;
    }
}

这里大家可能会有疑惑的地方在于,为什么要创建一个新的数组,同时将数组头部和尾部的值设置成0呢

这是为了防止数组也是单调递减或者单调递增的的,假如说我们遇到了这样的数组

那么一直找不到更小的元素,我们的单调栈岂不是没有用了? 

如果遇到这样的数组

以第一个柱子为基准,由于没有左边的柱子,那么又该怎么样来计算呢?

所以为了防止这样的情况发生,我们直接创建的一个新的数组,将数组头和尾设置成0方便计算


单调栈的题目其实并不难,直接记住了模板(参考每日温度)那么就是细节的处理了

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

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

相关文章

ARM 堆栈寻址类型区分

文章目录 堆栈指向分类堆栈指向数据分类满递增与满递减空递增与空递减 堆栈指向分类 根据堆栈指针的指向的方向不同&#xff0c;可以划分为向上生成型和向下生成型。 向上生成型&#xff1a; 随着数据的入栈&#xff0c;堆栈的指针逐渐增大&#xff0c;称为&#xff1a;递增…

数据结构中的七大排序(Java实现)

目录 一、直接插入排序 二、希尔排序 三、直接选择排序 四、堆排序 五、冒泡排序 六、快速排序 七、归并排序 一、直接插入排序 思想&#xff1a; 定义i下标之前的元素全部已经有序&#xff0c;遍历一遍要排序的数组&#xff0c;把i下标前的元素全部进行排序&#xff0…

Python 爬虫入门:常见工具介绍

接着我的上一篇文章《网页爬虫完全指南》&#xff0c;这篇文章将涵盖几乎所有的 Python 网页爬取工具。我们从最基本的开始讲起&#xff0c;逐步涉及到当前最前沿的技术&#xff0c;并且对它们的利弊进行分析。 当然&#xff0c;我们不能全面地介绍每个工具&#xff0c;但这篇…

C++string类重要函数模拟实现

为了和C标准库区分&#xff0c;以下代码除主函数外均在namespace空间 目录 一.成员 二、带参构造函数 三、拷贝构造函数和赋值运算符重载 四、析构函数 五、重要成员函数实现 1. c_str函数 2. operator[]重载 3. size函数和capacity函数 4.reverse函数 5. push_back和…

plink分析100个性状的批量gwas分析

大家好&#xff0c;我是邓飞。 GWAS分析时&#xff0c;3~5个性状是正常操作&#xff0c;要分析100个性状呢&#xff0c;手动修改参数&#xff0c;工作量是够了&#xff0c;但是程序员的修养体现在哪里了&#xff1f;&#xff1f;&#xff1f; 如果还是按照每个性状一个文件夹…

Jetpack:012-Jetpack中的弹出菜单

文章目录 1. 概念介绍2. 使用方法2.1 DropdownMenu2.2 DropdownMenuItem 3. 示例代码3.1 代码和注释3.2 代码难点3.3 运行效果 4. 内容总结 我们在上一章回中介绍了Jetpack中标题栏相关的内容&#xff0c;本章回中主要 弹出菜单。闲话休提&#xff0c;让我们一起Talk Android …

Appium+python+unittest搭建UI自动化框架!

阅读本小节&#xff0c;需要读者具备如下前提条件&#xff1a; 1. 掌握一种编程语言基础&#xff0c;如java、python等。 2. 掌握一种单元测试框架&#xff0c;如java语言的testng框架、python的unittest框架。 3. 掌握目前主流的UI测试框架&#xff0c;移动端APP测试框架Appiu…

智能化巡检系统哪家好?巡检系统可以为企业单位带来什么便利?

设备点检是设备维修策略中预防维修的一个重要手段。在很多单位内也得到了广泛的应用&#xff0c;但是实施效果均不太理想&#xff0c;弄虚作假的情况时常存在。尽管现在是人手一机的时代&#xff0c;但仍然有不少企业以纸笔抄录作为点检模式&#xff0c;这样就容易存在一系列的…

HarmonyOS/OpenHarmony原生应用-ArkTS万能卡片组件Slider

滑动条组件&#xff0c;通常用于快速调节设置值&#xff0c;如音量调节、亮度调节等应用场景。该组件从API Version 7开始支持。无子组件 一、接口 Slider(options?: {value?: number, min?: number, max?: number, step?: number, style?: SliderStyle, direction?: Ax…

如何确定IP地址的具体位置?

IP地址通过几种方法帮助确定具体位置&#xff0c;尽管它们的准确性和精度因不同的情况而异。以下是几种确定具体位置的主要方法&#xff1a; 地理IP数据库&#xff1a;这是最常用的方法之一&#xff0c;它使用IP地址和地理位置数据的映射来确定用户的位置。这些数据库存储了大量…

节省工时超 1500人/天,国泰基金探索金融业人机协同新业态

“十四五”时期是我国经济实现从高速增长转变为高质量发展的关键历史时期&#xff0c;“十四五”规划向金融行业提出了数字化转型与科技监管的新要求。在新一轮科技革命和产业变革趋势下&#xff0c;新一代信息技术与金融行业融合加速&#xff0c;金融行业面临着监管要求与自身…

coreldraw2018零售版最新下载步骤

安装前一定要先退出杀毒软件&#xff1a;360杀毒、360安全卫士、腾讯管家等。 第一步&#xff1a;打开安装包 CorelDRAW2018版win下载如下:https://wm.makeding.com/iclk/?zoneid55678 CorelDRAW2018版mac下载如下:https://wm.makeding.com/iclk/?zoneid55679 第二步&…

AutoSAR入门:开发工具链介绍

1、AutoSAR愿景/目标 AutoSAR的目标&#xff0c;旨在进行嵌入式软件的标准化。 2、AutoSAR在BMS中的应用 国外公司BMS 做的比较好的有联电、大陆、德尔福、AVL 和FEV 等等&#xff0c; 现在基本上都是按照AUTOSAR架构以及ISO26262功能安全的要求来做&#xff0c;软件功能更多&…

Flutter笔记:发布一个Flutter头像模块 easy_avatar

Flutter笔记 发布一个头像Flutter模块 easy_avatar 作者&#xff1a;李俊才 &#xff08;jcLee95&#xff09;&#xff1a;https://blog.csdn.net/qq_28550263 邮箱 &#xff1a;291148484163.com 本文地址&#xff1a;https://blog.csdn.net/qq_28550263/article/details/1339…

Stable Diffusion WebUI几种解决手崩溃的方法

1. 添加与手相关负面提示词 如何提价提示词呢? 首先有一个embeddings模型文件bad-hands-5,我们可以去各个大模型网站去搜,我是在C站上面下载的。 附上C站地址:https://civitai.com/ 下载好之后,你需要将文件放入stable-diffusion-webui\embeddings目录中。位置如下所示…

【Linux-常用命令-基础命令-解压rar文件-unrar-x-命令-笔记】

【Linux-常用命令-基础命令-解压rar文件-unrar-x-命令-笔记】 1、前言2、操作3、自己的操作 1、前言 最近&#xff0c;在使用Linux的时&#xff0c;使用相关基础命令是&#xff0c;总是容易忘记&#xff0c;上网一搜&#xff0c;大部分都写的比较繁琐&#xff0c;解压不同文件…

执法记录仪主板_基于MTK6877联发科5G主板方案

4G/5G智能执法记录仪是一种集成了先进的人工智能和传感器技术的设备&#xff0c;不仅可以记录事件发生的过程&#xff0c;还能够辅助工作人员进行人车识别、安全预警。这种记录仪使用联发科MT6877芯片作为主板方案&#xff0c;该芯片采用了6纳米工艺制程&#xff0c;拥有八核CP…

揭秘108个CMD命令,让你成为计算机大神

今天整理了一些cmd命令 &#x1f449;操作方法&#xff1a;快捷键winR&#xff0c;输入cmd回车&#xff0c;然后就可以输入cmd命令了&#xff0c;赶紧收藏起来&#xff0c;用的时候更方便 想了解更多网工知识&#xff0c;获取《网工大礼包》&#xff0c;可关注公众号&…

PTE-精听学习(二)

目录 时间分配 消音题可以帮忙 节约时间的 剩余9个题目一定要剩余出10分钟以上 MSA 单选题看时间 评分标准 ​编辑 SMW 单词长 空挡 会早一点 单词短 beep短 会晚一点 做题方法 1.先猜 2.不准记笔记 3.搭配 HIW 大分题不是小分题 评分标准 3-6个是比较常见…

zabbix-proxy代理服务器配置

下载zabbix源 rpm -Uvh https://repo.zabbix.com/zabbix/5.0/rhel/7/x86_64/zabbix-release-5.0-1.el7.noarch.rpm 安装 yum -y install zabbix-proxy-mysql zabbix_get 查看相关文件路径 rpm -ql zabbix-proxy-mysql 创建数据库 mysq -uroot -proot mysql> create database…