数据结构篇-01:单调栈

单调栈是栈的一种,可以使得每次新元素入栈后,栈内的元素都保持有序(单调递增或者单调递减)。

单调栈的用途不太广泛,只处理一类典型的问题,比如[下一个更大元素]、[上一个更小元素] 等。

在本文中,我将首先介绍 [单调栈] 的使用模板,接着我会使用单调栈的技巧来解决力扣hot100中的两道题:739、每日温度;84、柱状图中最大的矩形

单调栈的使用

例题:输入一个数组 nums,请你返回一个等长的结果数组,结果数组中对应索引存储着下一个更大元素,如果没有更大的元素,就存 -1。

函数签名如下:

int[] nextGreaterElement(int[] nums);

比如说,输入一个数组 nums = [2,1,2,4,3],你返回数组 [4,2,4,-1,-1]。因为第一个 2 后面比 2 大的数是 4; 1 后面比 1 大的数是 2;第二个 2 后面比 2 大的数是 4; 4 后面没有比 4 大的数,填 -1;3 后面没有比 3 大的数,填 -1。

int[] nextGreaterElement(int[] nums) {int n = nums.length;// 存放答案的数组int[] res = new int[n];Stack<Integer> s = new Stack<>(); // 倒着入栈,借助栈的结构,就是正着出栈for (int i = n - 1; i >= 0; i--) {// 对元素进行判定:如果当前元素不小于栈顶元素,则弹出//因为我们要找的是“最近的更大元素”while (!s.isEmpty() && s.peek() <= nums[i]) {s.pop();}// nums[i] 身后的更大元素//如果为-1,说明没有找到合适的元素res[i] = s.isEmpty() ? -1 : s.peek();s.push(nums[i]);}return res;
}

我们用文字来描述一下这个过程:

由于是倒着入栈,所以我们从 [3] 开始。[3] 后面没有比它大的元素,返回 -1,然后将 [3] 入栈;

接着是 [4] ,此时栈不为空,栈顶元素(3)小于 [4] 被弹出,栈被置为空。所以 res[i] = -1,将 [4] 压入栈中。

接着是 [2],此时栈不为空,栈顶元素 [4] 大于 [2] 满足条件,将其记录到res数组中,然后将 [2] 压入栈中。

接着是 [1],此时栈不为空,栈顶元素 [2] 大于 [1],满足条件,将其记录到res数组中,然后将 ]1\ 压入栈中。

最后是 [2],此时栈顶元素 [1] 小于 [2],弹出;弹出后,新的栈顶元素是 [2] ,不符合条件,弹出;新的栈顶元素变为 [4],满足条件,将其记录下来。并将 [2] 压入栈中

数组nums遍历结束,返回res数组。

问题1:为什么要倒着入栈?

从后往前入栈,保证了出栈的时候,栈顶元素一定是距离当前目标元素最近的元素。这样能实现寻找 “最近的更大元素”/"下一个更大元素"

比如数组 [1,2,3,4] 。当数组倒着遍历到2的时候,栈中的元素是 [4,3],那么出栈顺序就是 [3,4]。就是这样借助栈的性质来实现查找


力扣739:每日温度

 

比如说给你输入 temperatures = [73,74,75,71,69,76],你返回 [1,1,3,2,1,0]。因为第一天 73 华氏度,第二天 74 华氏度,比 73 大,所以对于第一天,只要等一天就能等到一个更暖和的气温,后面的同理。

这个问题本质上也是找下一个更大元素,只不过现在不是问你下一个更大元素的值是多少,而是问你当前元素距离下一个更大元素的索引距离而已。

所以我们要对上面单调栈的算法模板稍加修改:之前用于存放元素的数组res中不再用于记录元素,而是记录元素的索引

int[] dailyTemperatures(int[] temperatures) {int n = temperatures.length;int[] res = new int[n];// 这里放元素索引,而不是元素Stack<Integer> s = new Stack<>(); /* 单调栈模板 */for (int i = n - 1; i >= 0; i--) {while (!s.isEmpty() && temperatures[s.peek()] <= temperatures[i]) {s.pop();}// 得到索引间距res[i] = s.isEmpty() ? 0 : (s.peek() - i); // 将索引入栈,而不是元素s.push(i); }return res;
}

栈 s 中存放的是元素索引,所以当栈顶元素满足条件——温度高于当天温度时,栈顶元素意为:在这一天达到下一个更高的温度。

然后用栈顶元素减去当日日期就是相隔几天。

力扣84:柱状图中最大的矩形

对于该问题,暴力解法的思路是枚举每个柱子作为矩形的高度,并以该柱子为中心向左右延伸,直到遇到高度小于当前柱子的柱子为止。然后计算以当前柱子为高度的矩形的面积,并更新最大面积。

具体的实现步骤如下:

  1. 遍历每个柱子,将其作为矩形的高度(记为h)。
  2. 从当前柱子向左延伸,直到遇到高度小于h的柱子为止,记录左边界的位置(记为left)。
  3. 从当前柱子向右延伸,直到遇到高度小于h的柱子为止,记录右边界的位置(记为right)。
  4. 计算以当前柱子为高度的矩形的面积,即面积 = h * (right - left - 1)。
  5. 更新最大面积,如果当前面积大于最大面积,则更新最大面积。
  6. 重复以上步骤,直到遍历完所有柱子。

暴力方法的时间复杂度为 O(N^2),会超出时间限制,主要在于寻找左右边界上。而寻找左右边界,其实就是 “寻找最近的高度小于h的柱子”。可以使用单调栈解决。

需要注意的是,根据题目要求,我们要分别寻找左边界和右边界,所以我们要使用两次单调栈。

class Solution {public int largestRectangleArea(int[] heights) {int n = heights.length;int[] left = new int[n];   // 存储每个柱子左边第一个小于它的柱子的索引int[] right = new int[n];   // 存储每个柱子右边第一个小于它的柱子的索引Deque<Integer> mono_stack = new ArrayDeque<Integer>();  // 单调栈,存储柱子的索引//我们寻找右边界时是倒着入栈,那么寻找左边界时就是正着入栈for (int i = 0; i < n; ++i) {//在这里我们要寻找的是:当前柱子左侧的第一个小于它的索引。所以如果栈顶元素大于当前柱子高度(不符合条件),则弹出该元素while (!mono_stack.isEmpty() && heights[mono_stack.peek()] >= heights[i]) {mono_stack.pop(); }left[i] = (mono_stack.isEmpty() ? -1 : mono_stack.peek());   mono_stack.push(i);   // 将当前柱子的索引入栈}//清除栈中的元素,因为后面还要用这个栈mono_stack.clear();for (int i = n - 1; i >= 0; --i) {while (!mono_stack.isEmpty() && heights[mono_stack.peek()] >= heights[i]) {mono_stack.pop();   }right[i] = (mono_stack.isEmpty() ? n : mono_stack.peek()); mono_stack.push(i);   // 将当前柱子的索引入栈}int ans = 0;for (int i = 0; i < n; ++i) {// 计算每个柱子的面积并更新最大面积ans = Math.max(ans, (right[i] - left[i] - 1) * heights[i]); }return ans;}
}

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

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

相关文章

Pandas应用-股票分析实战

股票时间序列 时间序列&#xff1a; 金融领域最重要的数据类型之一 股价、汇率为常见的时间序列数据 趋势分析&#xff1a; 主要分析时间序列在某一方向上持续运动 在量化交易领域&#xff0c;我们通过统计手段对投资品的收益率进行时间序列建模&#xff0c;以此来预测未来的收…

六、VTK创建平面vtkPlaneSource

vtkPlaneSource创建位于平面中的四边形数组 先看看效果图: vtkPlaneSource 创建一个 m x n 个四边形数组,这些四边形在平面中排列为规则平铺。通过指定一个原点来定义平面,然后指定另外两个点,这两个点与原点一起定义平面的两个轴。这些轴不必是正交的 - 因此您可以创建平行…

css3表格练习

1.效果图 2.html <div class"line"></div><h3>获奖名单</h3><!-- 表格 cellspacing内边距 cellpadding外边距--><table cellspacing"0" cellpadding"0" ><!-- thead表头 --><thead><tr>…

蓝桥杯备战——6.串口通讯

1.分析原理图 由上图我们可以看到串口1通过CH340接到了USB口上&#xff0c;通过串口1我们就能跟电脑进行数据交互。 另外需要注意的是STC15F是有两组高速串口的&#xff0c;而且可以切换端口。 2.配置串口 由于比赛时间紧&#xff0c;我们最好不要去现场查寄存器手册&#x…

【搞懂设计模式】享元模式:共享节约,皆大欢喜!

什么是享元模式&#xff1f; 巧记&#xff1a;共享节约&#xff0c;皆大欢喜。 总结&#xff1a;享元模式是一种结构型设计模式&#xff0c;它通过共享技术有效地支持大量细粒度的对象。想象一个大家庭的餐桌&#xff0c;一顿饭需要的碗筷&#xff0c;如果每个人都自己带一套…

暴力破解

暴力破解工具使用汇总 1.查看密码加密方式 在线网站&#xff1a;https://cmd5.com/ http://www.158566.com/ https://encode.chahuo.com/kali&#xff1a;hash-identifier2.hydra 用于各种服务的账号密码爆破&#xff1a;FTP/Mysql/SSH/RDP...常用参数 -l name 指定破解登录…

MAX27——处理max模型导出Zbrush中,无UV,或者UV炸开,反向等问题。

现在很多小伙伴要做数字人的时候会用到zbrush拓补高模。制作法线。有些人喜欢在zbrush中去做封套。也有喜欢直接用max做低模&#xff0c;做好的uv导入到Zbrush中&#xff0c;直接把高模法线&#xff0c;烘焙到低模UV的。这里主要讲解以下max导出到zbrush中&#xff0c;UV炸开&a…

SQL注入:报错注入

SQL注入系列文章&#xff1a;初识SQL注入-CSDN博客 SQL注入&#xff1a;联合查询的三个绕过技巧-CSDN博客 目录 什么是报错注入&#xff1f; 报错注入常用的3个函数 UpdateXML ExtractValue Floor rand&#xff08;随机数&#xff09; floor&#xff08;向上取整&…

大模型日报-20240126

「think step by step」还不够&#xff0c;让模型「think more steps」更有用 https://mp.weixin.qq.com/s/3mdDYQI0WYgIpctyK6q2PA 如今&#xff0c;大型语言模型&#xff08;LLM&#xff09;及其高级提示策略的出现&#xff0c;标志着对语言模型的研究取得了重大进展&#x…

JavaScript 生成器(Generator)、高级 iteration用法详解

&#x1f9d1;‍&#x1f393; 个人主页&#xff1a;《爱蹦跶的大A阿》 &#x1f525;当前正在更新专栏&#xff1a;《VUE》 、《JavaScript保姆级教程》、《krpano》、《krpano中文文档》 ​ 目录 ✨ 前言 ✨ 正文 一、生成器(Generator) 什么是生成器 生成器函数 nex…

Unity 代理模式(实例详解)

文章目录 实例1&#xff1a;资源加载代理&#xff08;Asset Loading Proxy&#xff09;实例2&#xff1a;网络请求代理&#xff08;Network Request Proxy&#xff09;实例3&#xff1a;性能优化代理&#xff08;Performance Optimization Proxy&#xff09;实例4&#xff1a;权…

Docker容器部署OpenCV,打造高效可移植的计算机视觉开发环境

推荐 海鲸AI-ChatGPT4.0国内站点&#xff1a;https://www.atalk-ai.com 前言 在计算机视觉领域&#xff0c;快速部署和测试算法是研究和开发的关键。OpenCV作为一个强大的开源计算机视觉库&#xff0c;广泛应用于各种图像处理和视频分析任务。然而&#xff0c;配置OpenCV环境可…

光纤接口类型

光纤接口 网络设备基础知识 文章目录 光纤接口前言一、光纤接口二、光纤接口的优缺点总结前言 不同的接口类型适用于不同的光纤传输系统和应用需求。在选择光纤设备时,需要根据实际需求和系统要求选择适当的光纤接口类型。 一、光纤接口

计算机基础之微处理器简介

微处理器 微处理器定义 微型计算机的CPU也被称为微处理器&#xff0c;是将运算器、控制器和高速缓存集成在一起的超大规模集成电路芯片&#xff0c;是计算机的核心部件。能完成取指令、执行指令&#xff0c;以及与外界存储器和逻辑部件交换信息等操作。 微处理器发展 CPU从…

[C++]使用纯opencv部署yolov8旋转框目标检测

【官方框架地址】 https://github.com/ultralytics/ultralytics 【算法介绍】 YOLOv8是一种先进的对象检测算法&#xff0c;它通过单个神经网络实现了快速的物体检测。其中&#xff0c;旋转框检测是YOLOv8的一项重要特性&#xff0c;它可以有效地检测出不同方向和角度的物体。…

安全小记-ngnix负载均衡

目录 一.配置ngnix环境二.nginx负载均衡 一.配置ngnix环境 本次实验使用的是centos7,首先默认yum源已经配置好&#xff0c;没有配置好的自行访问阿里云镜像站 https://developer.aliyun.com/mirror/ 接着进行安装工作 1.首先创建Nginx的目录并进入&#xff1a; mkdir /soft &…

【七、centos要停止维护了,我选择Almalinux】

搜索镜像 https://developer.aliyun.com/mirror/?serviceTypemirror&tag%E7%B3%BB%E7%BB%9F&keywordalmalinux dvd是有界面操作的&#xff0c;minimal是最小化只有命里行 镜像下载地址 安装和centos基本一样的&#xff0c;操作命令也是一样的&#xff0c;有需要我…

openlayers+vue实现缓冲区

文章目录 前言一、准备二、初始化地图1、创建一个地图容器2、引入必须的类库3、地图初始化4、给地图增加底图 三、创建缓冲区1、引入需要的工具类库2、绘制方法 四、完整代码总结 前言 缓冲区是地理空间目标的一种影响范围或服务范围,是对选中的一组或一类地图要素(点、线或面…

C++面试:散列表

目录 1. 散列表的基本概念 散列表的定义 散列函数 哈希冲突 2. 处理冲突的方法 链地址法&#xff08;Separate Chaining&#xff09; 开放地址法 再散列 3. 散列表的性能分析 1. 平均查找长度&#xff08;ASL&#xff09; 2. 负载因子&#xff08;Load Factor&#…

【Linux】-cp模型

&#x1f496;作者&#xff1a;小树苗渴望变成参天大树&#x1f388; &#x1f389;作者宣言&#xff1a;认真写好每一篇博客&#x1f4a4; &#x1f38a;作者gitee:gitee✨ &#x1f49e;作者专栏&#xff1a;C语言,数据结构初阶,Linux,C 动态规划算法&#x1f384; 如 果 你 …