单调栈及单调栈的应用

article/2025/9/30 19:50:08

什么是单调栈

  • 单调递增栈:单调递增栈就是从栈底到栈顶数据是从大到小
  • 单调递减栈:单调递减栈就是从栈底到栈顶数据是从小到大

解决那类问题

   要知道单调栈的适用于解决什么样的问题,我们首先需要知道单调栈的作用。单调栈分为单调递增栈和单调递减栈,通过使用单调栈我们可以访问到下一个比他大(小)的元素(或者说可以)。也就是说在队列或数组中,我们需要通过比较前后元素的大小关系来解决问题时我们通常使用单调栈。下面我们通过简单介绍单调减栈和单调增栈问题来进一步说明使用单调栈处理问题的过程。

模拟实现一个递增单调栈:

 做一个从栈顶到栈底递减的单调栈,当栈为空或者是栈顶元素小于入栈元素时入栈,当栈不为空同时栈顶元素大于入栈元素时,pop栈顶元素,直到遇到入栈元素大于栈顶元素时,最后一次出栈的元素就是左端第一个比它小的数

10,3,7,4,12

 10入栈时,栈为空,直接入栈, 栈:10

3入栈时, 3<10 ,10出栈,此时栈为空,3入栈,栈:3

7入栈时,7>3,7入栈,  栈:3,7

4 入栈时,7>4,7出栈,3<4,4入栈,栈:3,4

12 入栈时, 12>4 ,入栈, 栈;3,4,12

伪代码

stack<int> st;
//此处一般需要给数组最后添加结束标志符,具体下面例题会有详细讲解
for (遍历这个数组)
{if (栈空 || 栈顶元素大于等于当前比较元素){入栈;}else{while (栈不为空 && 栈顶元素小于当前元素){栈顶元素出栈;更新结果;}当前数据入栈;}
}

例题

1,描叙:有n个人站队,所有的人全部向右看,个子高的可以看到个子低的发型,给出每个人的身高,问所有人能看到其他人发现总和是多少。
输入:4 3 7 1
输出:2
解释:个子为4的可以看到个子为3的发型,个子为7可以看到个子为1的身高,所以1+1=2
思路:观察题之后,我们发现实际上题目转化为找当前数字向右查找的第一个大于他的数字之间有多少个数字,然后将每个 结果累计就是答案,但是这里时间复杂度为O(N^2),所以我们使用单调栈来解决这个问题。

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

此题链接力扣题目,官方有给出动画演示,详细的很

    int trap(vector<int>& height) {int  ans=0;int current=0;stack<int >st;while(current<height.size()){while(!st.empty()&&height[current]>height[st.top()]){int t= st.top();st.pop();if(st.empty())break;int dtc=current-st.top()-1;int hght = min(height[current],height[st.top()])-height[t];ans+=dtc*hght;}st.push(current++);}return ans;}

 3,柱状图中的最大矩形

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

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

 

当前的数字是向两边扩展的,遇到比自己小的元素时出栈是向左扩展自己的边界,遇到自己大的,让大的进栈,向右扩展,记录最大的矩形。

int largestRectangleArea(vector<int>& heights) {heights.push_back(-1);/同理,我们希望栈中所有数据出栈,所以给数组最后添加一个负数stack<int> st;int ret = 0, top;for (int i = 0; i < heights.size(); i++){if (st.empty() || heights[st.top()] <= heights[i]){st.push(i);}else{while (!st.empty() && heights[st.top()] > heights[i]){top = st.top();st.pop();//i-top指的是当前矩形的宽度,heights[top]就是当前的高度//栈中现在为单调递增int tmp = (i - top)*heights[top];if (tmp > ret)ret = tmp;}st.push(top);heights[top] = heights[i];}}return ret;
}

st.push(top); heights[top] = heights[i]; //可能对这两句理解有问题

      可以这样子理解,在这里增加了一个宽度为(i-top),高度为hight[top]的虚拟的柱子,因为,就是后面有满足的区间,他不可能是比这个虚拟的柱子高的,你想,你想,尼好好想,是不是如此尼,哈哈哈哈

3.求最大区间

    描述:给出一组数字,求一区间,使得区间元素和乘以区间最小值最大,结果要求给出这个最大值和区间的左右端点
输入:3 1 6 4 5 2
输出:60
       3 5

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <stack>
#define ll long long
using namespace std;
const int maxn = 1e5+5;
int a[maxn];
ll sum[maxn];
stack<int> s;
int main(){int days;cin>>days;for(int i=1;i<=days;i++){scanf("%d",&a[i]);sum[i]=sum[i-1]+a[i];}a[days+1]=-1;ll maxa=-1;int pos1,pos2,f;for(int i=1;i<=days+1;i++){if(s.empty()||a[s.top()]<=a[i]) s.push(i);else{while(!s.empty()&&a[s.top()]>a[i]){f=s.top();s.pop();ll	tmp=sum[i-1]-sum[f-1];tmp*=a[f];if(tmp>maxa){maxa=tmp;pos1=f;pos2=i;}}s.push(f);//向前扩展a[f]=a[i];}}printf("%lld\n",maxa);printf("%d %d\n",pos1,pos2-1);return 0;
}

刷题时,扫到盲区了,找到了一个挺好理解这道题的博客

     https://www.cnblogs.com/ziyi--caolu/archive/2013/06/23/3151556.html

6
3 1 6 4 5 2

以4为最小值,向左右延伸,6 4 5  值为60.......

思路:解决完为这道题目,我才真正明白了单调栈的原理,它就是以某一个值为最小(最大)值,向这个值的两侧延伸,遇到大于它(小于它)的值,就将它延伸的范围扩大,当然,一般来说,要这样做的算法复杂度为o(n^2),但是借助栈这个玩意,维护其单调增(减),就可以在o(n)的时间复杂度解决这个问题。将一元素加入栈时,先判断它是否大于(小于)栈顶元素,若是大于(小于)栈顶元素,加入栈。(从这里开始只讲维护单调增栈)否则,将栈顶元素出栈,直到栈顶元素小于要加入栈的元素,在此过程中,需要维护向前延伸和向后延伸的问题,当要加入栈的元素之前有n个栈元素出栈,那么说明这n个出栈的元素都会大于或者等于要入栈的元素,此时,我们需要维护入栈元素可以向前延伸多少个元素(相当于记录它的前面有多少个元素比它大),而每个栈顶元素要向出栈了的元素延伸,因为在出栈了的元素一定是比它的大的元素(根据我维护的是单调增栈)......这样,就在o(n)的时间复杂度内解决了上述问题.........

例如:3 1 6 4 5 2

(3,1,1)  (1,2,2)  (6,3,3)  (4,4,4)  (5,5,5)  (2,6,6)

首先每个元素自己本身的前后延伸都为1,把3加入栈,1<3,把3出栈,用1的前延伸加上3的前延伸,如此变为(1,1,2),6<1,入栈,变成(1,1,2)(6,3,3),

4<6,将6出栈,4向前延伸,1向后延伸变成(1,1,3) (4,3,4) 

5>4,入栈,变成(1,1,3)(4,3,4)(5,5,5)

2<5,5出栈,2向前延伸,4向后延伸,变成(1,1,3)(4,3,5)      2还未入栈(2,5,6)

2<4,4出栈,2向前延伸,1向后延伸,变成(1,1,5) (2,3,6).....

一次类推,会发现最大的结果在(4,3,5)这里这意味着,以4为最小值的区间范围为3————5,也就是6 4 5

# include<stdio.h>
# include<stack>
#include <iostream>
#define ll long long
using namespace std;
ll sum[100010];
typedef struct
{int x,m;int a,b;
}p;
p a[100010];
stack<p>S;
int main()
{ll i,r,k,n,max,l;scanf("%d",&n);sum[0]=0;for(i=1;i<=n;i++){scanf("%d",&a[i].x);a[i].m=i;a[i].a=i;a[i].b=i;//每个元素都是自身的扩展sum[i]=sum[i-1]+a[i].x;}a[n+1].x=-1;a[n+1].m=n+1;for(i=1;i<=n+1;i++){while(S.empty()==0&&a[i].x<a[S.top().m].x){a[i].a=a[S.top().m].a;//i 向前扩展a[S.top().m].b=i-1;//s.top.m向后扩展S.pop();}S.push(a[i]);}// for(int i=1;i<=n;i++){//   cout<<a[i].x<<" "<<a[i].a<<" "<<a[i].b<<endl;//  }max=-1;for(i=1;i<=n;i++){k=(sum[a[i].b]-sum[a[i].a-1])*a[i].x;if(k>max){max=k;r=a[i].a;l=a[i].b;}}printf("%d\n%d %d",max,r,l);return 0;
}

总结:对单调栈有了一点理解,上述如果有错误,欢迎大佬们指正


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

相关文章

理解单调栈与单调队列

单调栈 单调栈&#xff1a;栈内的元素按照某种方式排序下单调递增或单调递减&#xff0c;如果新入栈的元素破坏的单调性&#xff0c;就弹出栈内元素&#xff0c;直到满足单调性。 单调栈分为单调递增栈和单调递减栈&#xff1a; 单调递增栈&#xff1a;栈中数据入栈或出栈的…

【栈 单调栈】浅谈单调栈与单调栈的理解

单调栈 定义&#xff1a; 单调栈&#xff0c;顾名思义&#xff0c;是栈内元素保持一定单调性&#xff08;单调递增或单调递减&#xff09;的栈。这里的单调递增或递减是指的从栈顶到栈底单调递增或递减。既然是栈&#xff0c;就满足后进先出的特点。与之相对应的是单调队列。 …

单调栈(一)

单调栈基本概念及实现 方案1&#xff1a;对于每一个数&#xff0c;遍历其左右位置&#xff0c;时间复杂度为O(N^2) 方案2&#xff1a;单调栈&#xff0c;每个元素入栈一次出栈一次&#xff0c;时间复杂度为O(N) &#xff08;一&#xff09;数组中没有重复值 示例&#xff1a;[…

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

单调栈与单调队列 一、单调栈1、什么是单调栈&#xff1f;2、单调栈的模板&#xff08;1&#xff09;问题&#xff1a;&#xff08;2&#xff09;分析&#xff1a; 二、单调队列1、什么是单调队列2、单调队列模板&#xff08;1&#xff09;问题&#xff08;2&#xff09;分析 一…

单调栈算法详解

单调栈算法详解 单调栈使用模板 stack<int> st; //此处一般需要给数组最后添加结束标志符&#xff0c;具体下面例题会有详细讲解 for (遍历这个数组){if (栈空 || 栈顶元素大于等于当前比较元素){入栈;}else{while (栈不为空 && 栈顶元素小于当前元素){栈顶元素…

单调队列和单调栈详解

这里是我的blog&#xff1a;有更多算法分享。排版可能也会更好看一点v https://endlesslethe.com/monotone-queue-and-stack-tutorial.html 前言 单调栈和单调队列算是栈和队列的高级应用吧&#xff0c;在公司面试中应该是不怎么会出现的&#xff08;除非算法岗&#xff1f;…

什么是单调栈

什么是单调栈 单调栈就是单调递增或者单调递减的栈&#xff0c;也就是栈底到栈顶递增或递减&#xff0c;根据单调栈的的这种结构&#xff0c;可以很容易想到运用单调栈可以很容易的把O(n)的时间复杂度优化到O(n),如果使用数组的话&#xff0c;相对的空间复杂度也不会太高 示例 …

Java实现之单调栈

目录 一.单调栈 二.每日温度 1.题目描述 2.问题分析 3.代码实现 三.下一个更大元素 I 1.题目描述 2.问题分析 3.代码实现 四.下一个更大元素 II 1.题目描述 2.问题分析 3.代码实现 一.单调栈 通常是一维数组&#xff0c;要寻找任一个元素的右边或者左边第一个比自…

[数据结构]单调栈

单调栈 这是笔者的第一篇博客&#xff0c;由于笔者自身水平的限制。用词可能不够准确&#xff0c;句子不太通顺&#xff0c;代码水平可能也不太行&#xff0c;敬请指出&#xff0c;感激不尽&#xff01; 我们都知道栈&#xff08;Stack&#xff09;是一种先入后出的数据结构&am…

单调栈和单调队列

本文摘自博客&#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;通过使用单调栈我们可以访…