<>单调栈

我们从力扣84题的解决思路中引出单调栈的概念,首先看力扣84题的题目描述如下:
关于暴力解
:这道题是要寻找最大的而矩形区域,暴力的解法是:以每一个柱子为基准,寻找包含该柱子的最大矩形区域。即遍历到每一个柱子时,寻找以它的高度为高,宽度最大的矩形。这样的做法十分简单,但是遍历柱子的时间复杂度是O(N),而寻找包含每一根柱子的矩形最大宽度的时间复杂度也是O(N),这样一来总的算法的时间复杂度是O(N^2)

算法的改进:能否将该问题的时间复杂度缩减到O(N)?即我们尽可能地通过一次对所有柱子的遍历来解决问题。
问题的关键还是寻找一个以第i个柱子的高度为高的矩形的最大宽度,即就是寻找一个不大于这一高度的位置索引的左界限与有界限。

关于右界限的寻找
:右界限的寻找可以通过直接遍历即可,这里假设有一个栈,对于第i个柱子,我们想要寻找它的右界限,那么有两种可能,即它右边的下一个柱子高度比它小,那么下一个柱子就成了它的右界限;而如果下一个柱子的高度比他大,那么它的右界限就还是未知的。直到,碰到遍历到一个高度比它小的柱子,那么这个柱子的位置就成了它的右界限。

*
右边界的寻找过程可以具化成一个栈的操作:即当栈空时,遍历到的柱子入栈,当下一个遍历到的柱子比栈顶柱子高时,此时没有任何柱子的右边界可以确定,所以继续入栈;当遍历到的柱子比栈顶柱子低时,那么此时栈顶柱子的右边界就被确定了。将栈顶元素出栈计算宽度,然后再看新的栈顶元素会不会因为刚才这个遍历到的柱子的出现而被确定右边界,即比较新栈顶柱子和遍历到的柱子的高度,重复上述操作。
关于左界限的寻找
:左界限可以是一种累计,即之前比它大的元素都可以被累计到左边界的长度中,也可以是一种记录,记录左边比它小的数的位置。基于上述栈的操作,如果要记录左边第一个比它小的柱子的位置,分两种情况,如果它不是栈中最后一个元素,那么它下面压着的那个比它小的元素的位置就是它的左界限,如果它已经是栈中最后一个元素了,说明前面的元素全部都比它大,已在它前面全部出栈了,所以它的左界限可以延伸到数组最前面。

单调栈的概念
:首先单调栈是一种栈类型的数据结构,拥有栈所拥有的一切基本操作。单调栈的特殊之处在于它符合这样的特性:栈中的元素从栈底到栈顶的排列顺序是单调有序的,如果从栈底到栈顶,元素单调递减,我们称之为递增栈,因为在出栈时,元素的顺序是递增的;而如果从栈底到栈顶元素是递增的,那么称之为递减栈。

单调栈在入栈时必须保持单调性,如果将要入栈的元素不符合单调性,那么我们将栈顶元素弹出,直到让入栈元素入栈后可以符合单调性为止。以单调递减栈为例,加入入栈元素依次为1
2 5 4,那么前三个元素1 2 5
可以直接入栈,但是当元素4将要入栈时,会发现栈顶元素5大于入栈元素4,所以我们要先将5出栈,然后发现此时的新栈顶元素2小于4,则此时可以入栈。
int stack[1000000]; int top=0; void push(int index) { stack[top++]=index; } int
Pop() { return stack[--top]; } int Top() { return stack[top-1]; } int isEmpty()
{ if(top==0) return 1; else return 0; } int largestRectangleArea(int* heights,
int heightsSize){ int i=0,max=0,temp=0,pre=0; while(i<heightsSize) { if(isEmpty(
)||heights[Top()]<=heights[i]) { push(i++); } else{ if(top==1) { temp=i*heights[
Pop()]; if(max<temp) max=temp; } else{ temp=(i-stack[top-2]-1)*heights[Pop()];
if(max<temp) max=temp; } } } while(!isEmpty()) { if(top==1) { temp=i*heights[Pop
()]; if(max<temp) max=temp; } else{ temp=(i-stack[top-2]-1)*heights[Pop()]; if(
max<temp) max=temp; } } return max; }
有了以上的分析,基于单调栈的算法实现就很简单了。

我们挨个遍历数组中的元素,如果栈空或者不小于栈顶元素就入栈,否则将栈顶元素出栈,出栈的同时计算基于该柱子的矩形面积,即左右界限之间形成的宽度与该矩形的高度相乘。(在计算左右界限间宽度时要加以判断,看出栈的元素是否为栈底元素)
当遍历结束后,如果栈中还有元素,则依次出栈计算面积,最终将结果更新为最大面积即可。

技术
下载桌面版
GitHub
Gitee
SourceForge
百度网盘(提取码:draw)
云服务器优惠
华为云优惠券
腾讯云优惠券
阿里云优惠券
Vultr优惠券
站点信息
问题反馈
邮箱:[email protected]
吐槽一下
QQ群:766591547
关注微信