单调栈

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

什么是单调栈?

从名字上就听的出来,单调栈中存放的数据应该是有序的,所以单调栈也分为单调递增栈单调递减栈

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

模拟单调栈的数据push和pop

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

现在有一组数10,3,7,4,12。从左到右依次入栈,则如果栈为空入栈元素值小于栈顶元素值,则入栈;否则,如果入栈则会破坏栈的单调性,则需要把比入栈元素小的元素全部出栈。单调递减的栈反之。

  • 10入栈时,栈为空,直接入栈,栈内元素为10。

  • 3入栈时,栈顶元素10比3大,则入栈,栈内元素为10,3。

  • 7入栈时,栈顶元素3比7小,则栈顶元素出栈,此时栈顶元素为10,比7大,则7入栈,栈内元素为10,7。

  • 4入栈时,栈顶元素7比4大,则入栈,栈内元素为10,7,4。

  • 12入栈时,栈顶元素4比12小,4出栈,此时栈顶元素为7,仍比12小,栈顶元素7继续出栈,此时栈顶元素为10,仍比12小,10出栈,此时栈为空,12入栈,栈内元素为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),所以我们使用单调栈来解决这个问题。

1.设置一个单调递增的栈(栈内0~n为单调递减)
2.当遇到大于栈顶的元素,开始更新之前不高于当前人所能看到的值

int FieldSum(vector<int>& v)
{v.push_back(INT_MAX);/这里可以理解为需要一个无限高的人挡住栈中的人,不然栈中元素最后无法完全出栈stack<int> st;int sum = 0;for (int i = 0; i < (int)v.size(); i++){if (st.empty() || v[st.top()] > v[i])//小于栈顶元素入栈{st.push(i);}else{while (!st.empty() && v[st.top()] <= v[i]){int top = st.top();//取出栈顶元素st.pop();sum += (i - top - 1);//这里需要多减一个1}st.push(i);}}return sum;
}

2.柱状图中的最大矩形

https://leetcode-cn.com/problems/largest-rectangle-in-histogram/


思路:当前的数字可以向两边拓展,遇到比自己大的就接着拓展,小的就停止,然后用自己的高度乘以拓展的宽度,每次都         跟新最大面积,时间复杂度同样为O(N^2),所以我们接着借助单调栈

上面使用了单调递增栈,这里我们通过这道例题来使用一下单调递减栈

1.设置一个单调递减的栈(栈内0~n为单调递增)
2.当遇到小于栈顶元素的值,我们开始更新数据,因为有可能最大面积就会出现在栈中的序列里
3.牢记栈中数据永远是有序的,这个问题比较复杂,所以读者不妨对照着代码来理解问题

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;
}

很多读者不太懂下图这两句代码:

假设遇到了小于栈顶的数据,我们需要判断下图中哪个矩形更大,并且跟新数据,这里应该都可以理解,我们将图中三个数据标记为0,1,2.接着往下看

因为需要保持栈中递增的属性,所以栈中只有i一个数据:

但是对于当前元素来说下标为0,1的元素都比他大,所以那么就意味着它可以向左延申扩大矩形:像下图那样

但是我们为了保持栈中的递增属性,并且可以让i可以向左拓展,我们索性修改了i的下标,将他修改为最左边的top下标,所以当我们下次需要以他为基准获取矩形面积时就像这样

所以假设我们数组中的4个数据(实际是5个,最后一个数字用来出栈所有数据)全部访问完时:如下面的方式计算矩形

ps:如果有的同学还是不清楚,可以用自己的编译器调试一下。

3.求最大区间

描述:给出一组数字,求一区间,使得区间元素和乘以区间最小值最大,结果要求给出这个最大值和区间的左右端点
输入:3 1 6 4 5 2
输出:60
       3 5
解释:将3到5(6+4+5)这段区间相加,将和与区间内最小元素相乘获得最大数字60
思路:使用暴力解法求出所有区间,再求出区间的最小值相乘跟新数据,并不是一种很好的算法,所以经过上面俩题的磨         炼,此时我们应该使用一个单调递减栈

1.设置一个单调递减的栈(栈内0~n为单调递增)
2.当遇到小于栈顶元素的值,我们开始更新数据,因为当前遇到的值一定是当前序列最小的

int GetMaxSequence(vector<int>& v)
{stack<int> st;vector<int> vs(v.size()+1);vs[0] = 0;for (int i = 1; i < vs.size(); i++){vs[i] = vs[i - 1] + v[i-1];}v.push_back(-1);int top, start, end, ret = 0;for (int i = 0; i < v.size(); i++){if (st.empty() || v[st.top()] <= v[i]){st.push(i);}else{while (!st.empty() && v[st.top()] > v[i]){top = st.top();st.pop();int tmp = vs[i] - vs[top];tmp = tmp * v[top];if (tmp > ret){ret = tmp;start = top+1;end = i;}}st.push(top);v[top] = v[i];//与第二题相同的道理,将当前数据的更改最左的top下标,防止出现比当前数据更小的数据//这句在这道题里真的超级难理解,但是只要你有耐心相信你可以理解的}}return ret
}

总结

单调栈是帮助我们完成算法的一个数据结构,很多的题中还是单调栈的身影,更多需要单调栈的题就希望读者自己去发现啦,文章如果有什么问题或者建议希望广大读者们可以提出。

[数据结构]——单调栈相关推荐

  1. 数据结构 - 单调栈、单调队列

    单调栈:每日温度 请根据每日 气温 列表 temperatures ,请计算在每一天需要等几天才会有更高的温度.如果气温在这之后都不会升高,请在该位置用 0 来代替 单调栈基本只处理NGE问题(Nex ...

  2. 数据结构 单调栈+几何 摩天大楼【HDU 5033 】

    HDU 5033 题目大意: 就是一个人来到充满摩天大楼的城市,所有大楼没有宽度.建一直角坐标系,给出每个建筑的高度,现在求人站在(x,0)处能够看到天空的范围.(即不被摩天大楼阻挡).答案只需要给出 ...

  3. 股票价格跨度--单调栈

    leetcode 901:力扣 今天做这道题时,第一次了解到单调栈这种方法,就是用栈维护一个单调递增或单调递减的序列 本题,需要找到的是一个股票价格的跨度,常规想法就是从后往前查找,对小于等于当前股票 ...

  4. 数据结构录 之 单调队列单调栈。

    队列和栈是很常见的应用,大部分算法中都能见到他们的影子. 而单纯的队列和栈经常不能满足需求,所以需要一些很神奇的队列和栈的扩展. 其中最出名的应该是优先队列吧我觉得,然后还有两种比较小众的扩展就是单调 ...

  5. 蒟蒻的ACM数据结构(四)-单调队列和单调栈

    单调队列和单调栈 一.概念 二.实现 三.题目 单调队列 洛谷P1886 滑动窗口 解析 单调栈 [GXOI/GZOI2019]与或和 解析 POJ3250 Bad Hair Day 解析 POJ 2 ...

  6. 备战NOI 数据结构——栈与单调栈(stack) 以及后缀表达式

    栈 stack 引入 栈的概念 代码实现 定义和初始化(init) 入栈(push) 出栈(pop) 访问栈顶元素(query) 查询栈的元素个数(size) 判断是否为空(empty) 清空栈(cl ...

  7. 算法笔记(三)特殊数据结构——哈希表、有序表、并查集、KMP、Manacher、单调栈、位图、大数据类题

    layout: post title: 算法笔记(三)特殊数据结构--哈希表.有序表.并查集.KMP.Manacher.单调栈.位图.大数据类题 description: 算法笔记(三)特殊数据结构- ...

  8. 0x11.基本数据结构 — 栈与单调栈

    目录 一.栈 0.AcWing 41. 包含min函数的栈 (自己造栈) 1.AcWing 128. 编辑器 (对顶栈) 2.AcWing 129. 火车进栈 3.AcWing 130. 火车进出栈问 ...

  9. 【数据结构】单调栈和单调队列 详解+例题剖析

    算法:单调栈和单调队列 一.单调栈和单调队列 二.单调栈例题 1.模板题入门 2.不懂不要急,看这道题 三.单调队列例题 1.入门 2.进阶 一.单调栈和单调队列 单调栈和单调队列与普通的栈,队列不同 ...

最新文章

  1. 【脚下有根】之Skia库的matrix代码解读
  2. iptables的SNAT和DNAT应用
  3. YouTube上最火的OpenCV-Python入门视频教程
  4. 创建 Oracle 物理备用服务器
  5. Asp.Net无刷新分页( jquery.pagination.js)
  6. oracle的dos登陆口令,ORACLE的DOS操作方式
  7. 阿里云网盘开启公测!不限速、2T永久免费空间!!
  8. 硅谷裁员潮下的华人码农
  9. python实现食品推荐_通过Python语言实现美团美食商家数据抓取
  10. 10天学会PHP之PHP快速入门
  11. js原生写图片轮播和切换
  12. Nginx配置基于IP的访问控制
  13. 参数维纳滤波(Parametric Wiener Filter)
  14. python学习-109-多个CSV文件的合并
  15. shell脚本获取文件中的版权(Copyright)
  16. 【程序设计】程序错误与异常处理
  17. 【插件】前端表格在线表格类Excel插件推荐
  18. 利用计算机发布调度命令时必须严格遵守,复习题行车规章复习题
  19. 区块链、货币和社交扩展性 1
  20. 计算机网络三级考试分数划分,计算机等级考试三级网络技术题型分布

热门文章

  1. web前端 html+css+javascript网页设计实例 水果网站制作
  2. swep在C语言中的头文件,C ++ STL中的queue :: swap()
  3. Git与SVN的区别Git常用命令
  4. 利用cocos2dx 3.2开发消灭星星(四)游戏主场景
  5. linux中修改硬盘分区的卷标
  6. vue-pc打开本地摄像头并对画面进行截图
  7. uniapp制作pc端响应式布局——带开源前端【伸手党福利】【持续更新】
  8. Vue非父子组件传值
  9. matlab微积分计算
  10. BFS——山峰与山谷