[数据结构]单调栈

article/2025/9/30 22:05:57

单调栈

这是笔者的第一篇博客,由于笔者自身水平的限制。用词可能不够准确,句子不太通顺,代码水平可能也不太行,敬请指出,感激不尽!

我们都知道栈(Stack)是一种先入后出的数据结构,而单调栈建立在栈的基础上,它区别于普通的栈的特殊之处在于

栈中的元素一直保持着单调递增/单调递减的关系

比如单调递增栈中的元素自栈底至栈顶元素(0~n-1)间都是单调递增的,单调递减栈中的元素都是单调递减的。

为了加深印象,我们可以先以单调递减栈为例,看一个例子~

有一组数nums[6] = {10,1,8,12,6,4}按照如下规则进行操作:

  1. 如果栈为空/正在处理的数小于栈顶的数,则进栈;
  2. 如果正在处理的数大于栈顶的数,则将栈顶元素出栈,然后继续进行判断。

OK,首先我们将栈置空,然后再来一个一个地读数。

  1. 首先读到了10,此时栈为空,10进栈,现在栈的状态: {10}

  2. 读到了1,栈顶元素为10,1<10,1进栈,栈的状态: {10 , 1}

  3. 读到8,栈顶元素为1,8>1,那么我们将1出栈;

    继续判断,8<10,所以8进栈,栈的状态: {10,8}

  4. 读到数12,栈顶元素为8,12>8,将8出栈;

    继续判断,12>10,所以10出栈,此时栈已为空,将12进栈: {12}

  5. 读到6,6<12,进栈;{12,6}

  6. 读到4,4<6,将4进栈。 {12,6,4}

读到这边,相信读者对单调栈的基本操作已经稍微有了解啦,那么,单调栈有什么用呢?

还是看上面的例子,假设我们用单调栈处理上面的数组并且得到另外一个数组(假设数组名为arr,并且将该数组初始化为0),规定:在处理每个数(nums[i])之后,如果nums[i]保持在栈中,则将arr[i]自增1,那么处理完整个数组之后,得到的arr数组应该是:{3,1,1,3,2,1},不难发现,arr[i]代表的含义是:

自第i个元素开始,从左往右数的连续的比它小的数的个数

通过单调栈的数据结构,我们可以很容易地得到这个数组(很容易知道一个元素附近的数和它之间的大小关系)(只需要O(n)的时间)。所以,如果问题中经常需要判断数组前后元素的大小关系,不妨采用单调栈的数据结构。

这样看可能读者理解不够透彻,印象不够深刻,那不妨一起来看道例题吧~

接雨水

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

示例 1:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KKSNHLab-1603381702730)(C:\Users\86159\Desktop\rainwatertrap.png)]

输入:height = [0,1,0,2,1,0,1,3,2,1,2,1] 输出:6 解释:上面是由数组
[0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图,在这种情况下,可以接 6 个单位的雨水(蓝色部分表示雨水)。

示例 2:

输入:height = [4,2,0,3,2,5]
输出:9

看完题目之后我们可以先来分析一下,按照从左边开始数第i个位置上可以盛放的水的量 = min(该柱子左边最高柱子的高度,该柱子右边最高柱子的高度) - 该柱子的高度。

方法一:

自左往右暴力枚举每个柱子左边最高柱子的高度和右边最高柱子的高度,相加最后可得结果。时间复杂度为O(n²)。

方法二:

不难看出我们需要经常比较每个位置柱子高度和其左右柱子高度,所以不妨建一个单调栈来模拟这个过程:

(为了方便看,我们假设目前读到的柱子高度为heights[i],栈中存放柱子的编号(自左往右0~n-1),栈的高度为Size)

  1. 若栈为空(Size==0)或 柱子高度小于栈顶柱子高度(heights[i] < heights[stack[Size-1]]),则进栈;
  2. 若柱子高度大于等于栈底柱子(为此时左边最高的柱子)高度(heights[i] >= heights[stack[0]]),栈中除栈底外的每个格子可以多盛放heights[stack[0]] - heights[stack[j]] 个单位的雨水;
  3. 若柱子高度大于栈顶柱子高度且小于栈底柱子高度(heights[i] >= heights[stack[Size-1]]),则栈顶元素对应的柱子可以多盛放heights[i] - heights[stack[Size - 1]]个单位的雨水。(在更新了答案ans的值后,我们不妨令栈顶元素对应的柱子高度等于heights[i])

就上方第一个例子做个示范:

  1. 读入高度heights[0] = 0,进栈。Alt

  2. 读入高度heights[1] = 1,大于栈底元素,将栈底元素出栈(因为此时栈中只有一个元素,所以不需要对ans进行操作),再将heights[1]进栈。Alt

  3. 读入元素heights[2] = 0,小于栈顶元素,进栈。
    Alt

  4. 读入元素heights[3] = 2,大于栈底元素,将栈中所有元素弹出,此时因为heights[2] = 0 而 heights[1] = 1 ,所以可以多盛 1-0=1个单位的雨水,将ans + 1。然后将heights[3]进栈。
    Alt

  5. 读入元素heights[4] = 1,进栈。读入元素heights[5] = 0,进栈。
    Alt

  6. 读入元素heights[6] = 1,这里heights[6] > heights[5] 且heights[6] < heights[3] 。我们将ans加上heights[6] - heights[5]之后,令heights[5] = heights[6] 。继续判断,heights[4] = heights[6] 。 heights[6]进栈。
    Alt

  7. 读入元素heights[7] = 3,这里heights[7] > heights[3] (栈底元素)。所以要将栈中所有元素弹出,heights[4],heights[5],heights[6] 加上其与heights[3] 的差值,并且更新ans。然后将heights[7]进栈。
    Alt

  8. 后面的步骤都只是重复前面的,所以这边直接跳过。值得注意的是,最后栈中仍有五个元素,但因为右边是空的,无法盛水,所以无需出栈,保持原样即可。

    代码实现(C语言):

    int trap(int* height, int heightSize){int stack[1000];int top = 0,i;int ans = 0;for(i=0;i<heightSize;i++){if(top==0){	//如果栈为空,直接进栈stack[0] = i;top++;}else if(height[i] >= height[stack[0]]){ //如果读到元素大于栈底元素高度,将栈置空后进栈int j;for(j=1;j<top;j++){ans += height[stack[0]] - height[stack[j]];height[stack[j]] = heights[stack[0]]}stack[0] = i;top = 1;}else{int j=1;while(height[i]>height[stack[top-j]]){ ans += height[i] - height[stack[top-j]];height[stack[top-j]] = height[i];j++;}stack[top] = i;top++;}}return ans;
    }
    

限于笔者的水平,文章或者代码中可能存在错误或者疏漏,请读者不吝指正,感激不尽!


http://chatgpt.dhexx.cn/article/fGQjna3c.shtml

相关文章

单调栈和单调队列

本文摘自博客&#xff0c;欢迎前往博客以获得更好的体验。 单调栈 从名字上就听的出来&#xff0c;单调栈中存放的数据应该是严格单调有序的&#xff0c;具有以下两个性质。 满足从栈顶到栈底的元素具有严格的单调递增或单调递减性&#xff1b;满足栈的后进先出特性&#xff…

数据结构之单调栈(含代码实现)

目录 1.单调栈的基本概念 &#xff1a; 2.单调栈的应用 2.1单调栈 2.2单调栈进阶 2.3最大矩形面积 2.4最大矩形 2.5统计全为1的子矩阵数量 ​ 1.单调栈的基本概念 &#xff1a; 相信大家对栈都非常的熟悉&#xff1f;栈有一个非常鲜明的特点&#xff1a;先进后出 而所谓 单调栈…

C++之单调栈

单调栈的性质 单调栈是一种特殊的栈&#xff0c;特殊之处在于栈内的元素都保持一个单调性。 假设下图是一个栈内元素的排列情况(单调递增的栈)&#xff1a; 此时插入情况有两种&#xff1a; &#xff08;1&#xff09;插入元素大于栈顶元素&#xff1a; 因为7 > 6&#xf…

单调栈以及单调栈的应用

文章目录 单调栈的概念单调栈的应用CD101 单调栈结构&#xff08;无重复值&#xff09;CD188 单调栈结构(有重复值)496. 下一个更大元素 I739. 每日温度1856. 子数组最小乘积的最大值84. 柱状图中最大的矩形85. 最大矩形1504. 统计全 1 子矩形907. 子数组的最小值之和1307 验证…

单调栈完全解析

目录 单调栈的应用场景 为什么要使用单调栈&#xff1f; 单调栈作用的基本过程 单调栈的实现方式 栈里面的元素存放数字下标&#xff08;无重复元素&#xff09; 栈里面的元素存放数字下标组成的链表 &#xff08;有重复元素&#xff09; 单调栈的应用题目 直方图类型 …

单调栈与单调队列

文章目录 单调栈与单调队列一、单调栈1.单调递增栈2.单调递减栈总结 二、单调队列(单调双端队列) 单调栈与单调队列总结&#xff1a; 单调栈与单调队列 单调栈就是栈内元素满足单调性的栈结构。此处的单调性分为单调递增与单调递减 如何维护一个单调栈&#xff1a; 单调递增栈…

详解单调栈算法

前言 嘿&#xff01;彩蛋&#xff01;感觉有帮助就三连呗&#xff01; 如果你对这篇文章可感兴趣&#xff0c;可以点击「【访客必读 - 指引页】一文囊括主页内所有高质量博客」&#xff0c;查看完整博客分类与对应链接。 栈属于基础数据结构之一&#xff0c;基础到仅用「后进…

单调栈

定义&#xff1a; 单调栈&#xff0c;顾名思义就是栈内元素单调按照递增(递减)顺序排列的栈。 单调递增栈&#xff1a; ①在一个队列中针对每一个元素从它右边寻找第一个比它小的元素 ②在一个队列中针对每一个元素从它左边寻找第一个比它小的元素 单调递减栈&#xff1a; …

单调栈(C/C++)

目录 1. 单调栈的定义 2. 单调栈的常见用途 3. 案例分析 3.1 暴力解法 3.2 单调栈 4. 单调栈总结 1. 单调栈的定义 单调栈顾名思义&#xff0c;就是栈内的元素是单调的。根据栈内元素的单调性的不同&#xff0c;可以分为&#xff1a; 单调递增栈&#xff1a;栈内元素是单…

【算法】单调栈

目录 单调栈的定义&#xff1a;伪代码&#xff1a;应用1.模板题2.视野总和问题3.柱状图中的最大矩形4.最大区间 碎碎念&#xff1a; 单调栈的定义&#xff1a; 从名字上就能猜出来&#xff0c;这种数据结构在栈的基础上&#xff0c;栈内的元素是单调有序的&#xff0c;所以单调…

单调栈详解

定义&#xff1a; 单调栈&#xff0c;顾名思义就是栈内元素单调按照递增(递减)顺序排列的栈。 适用问题&#xff1a; 要知道单调栈的适用于解决什么样的问题&#xff0c;我们首先需要知道单调栈的作用。单调栈分为单调递增栈和单调递减栈&#xff0c;通过使用单调栈我们可以访…

[数据结构]——单调栈

单调栈 笔者在做leetcode的题(下一个出现的最大数字)时&#xff0c;接触到了单调栈这一种数据结构&#xff0c;经过研究之后&#xff0c;发现单调栈在解决某些问题时出奇的好用&#xff0c;下面是对单调栈的性质和一些典型题目。 什么是单调栈&#xff1f; 从名字上就听的出…

scp出现错误的解决办法

scp往远程主机上传送rmandata时报了如下错误&#xff1a; (看不见的放大) 我的做法是&#xff1a; 在执行scp的主机提示的scp目录 vi ~/.ssh/known_hosts 删除我红色部分遮盖的主机ip那一行&#xff0c;故障解决 来自 “ ITPUB博客 ” &#xff0c;链接&#xff1a;http://blo…

关于 fatal error LNK1158: 无法运行“rc.exe” 的解决方法

若该文为原创文章&#xff0c;转载请注明原文出处 本文章博客地址&#xff1a;https://blog.csdn.net/qq21497936/article/details/110680001 各位读者&#xff0c;知识无穷而人力有穷&#xff0c;要么改需求&#xff0c;要么找专业人士&#xff0c;要么自己研究 红胖子(红模仿…

VS发生RC1107错误的原因

最近MFC程序中&#xff0c;用VS的资源编辑打开时&#xff0c;老是发生 fatal error RC1107: invalid usage; use RC /? for Help 这种错误&#xff0c;记得前几天解决过一次&#xff0c;但是当时忘了怎么解决的了。今天每建一个新的工程都遇到这个问题&#xff0c;郁闷坏了&a…

错误 LINK : fatal error LNK1158: 无法运行“rc.exe”

2019独角兽企业重金招聘Python工程师标准>>> 问题 软件环境&#xff1a;Windows 10 Pro Visual Studio 2015 然后安装了 Windows 10 SDK Windows 10 SDK 是用这个 ISO 文件安装的&#xff1a;17134.12.180419-0858.rs4_release_svc_prod2_WindowsSDK.iso 在 Visual…

[rtsp @ 0x55ba1dae9200] UDP timeout, retrying with TCP的解决办法

使用ffmpeg 进行解码rtsp的时候出现: [rtsp @ 0x55ba1dae9200] UDP timeout, retrying with TCP如下所示: 需要使用接口指定以下tcp连接就可以解决了。 具体的代码如下: // rtsp:tcpAVDictionary* options = NULL;av_dict_set(&options, "rtsp_transport", …

brpc源码解析(二)—— brpc收到请求的处理过程

文章目录 一、基本设计思路二、实现细节三、总结 作为rpc服务器&#xff0c;在启动过后&#xff0c;最主要的一个过程就是收到请求后的处理&#xff0c;而这就牵涉到一个网络编程相关最基本的部分&#xff1a;如何有效地处理socket传过来地数据&#xff0c;这篇文章就来详细聊一…

libpng warning iCCP 错误处理方法

png图片缺乏某些库&#xff0c;导致损坏&#xff0c;或者多余了一些数据会导致以下报错&#xff1a; libpng warning: iCCP: known incorrect sRGB profile libpng warning iccp extra compressed data一些可能的解决方案&#xff1a; 已有方案 来自&#xff1a;https://blo…

创建线程提示SCB_CFSR_BFSR:0x04 IMPRECISERR 错误

在RTthread的编程出现了问题 这种问题并不是系统出现的问题&#xff0c;而是在处理自己的函数内部出现了数组越界&#xff0c;内存出现错误导致的。 关键还是自己指针访问的非法访问导致这些问题。遇到问题还是要多检查自己写的接口问题。