LeetCode每日一题题解:84. 柱状图中最大的矩形-题解-python && C++源代码

42 篇文章 1 订阅

84. 柱状图中最大的矩形

难度困难1782收藏分享切换为英文接收动态反馈

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

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

示例 1:

输入:heights = [2,1,5,6,2,3]
输出:10
解释:最大的矩形为图中红色区域,面积为 10

示例 2:

输入: heights = [2,4]
输出: 4

提示:

  • 1 <= heights.length <=105
  • 0 <= heights[i] <= 104

题目思路:

单调栈的意思可以看这个链接,这位大佬写的很详细https://blog.csdn.net/liujian20150808/article/details/50752861
单调栈,可以理解为有单调性的一个数组,在这里我们可以理解成为栈数组,栈数组可以是单调增,也可以是单调减,在本题中我们使用的是单调增
即栈数组从0到数组最后是单调递增的。单调栈的数组性质也是我们常用的性质,到这里为止,我们对单调栈有了基本印象,一个单调递的数组
从栈底到栈顶,单调递增,可以理解为从栈数组的0位置开始到栈数组的最后一位一直是单调递增。
**单调栈的性质:**接下来我们分析单调栈的性质,因为单调栈是即栈数组是单调递增的,因此他的单调性必须一直保持,因此在遇到下一个需要插入
栈数组的值的时候,我们首先判断该值与栈顶元素相比那个大?我们设栈顶元素为a,需要插入的值为b,当a<b的时候,我们可以直接将b插入栈数组
,我们可以设原来的栈数组为[0 , a],0到a单调递增,符合单调栈的要求
1:a<b的时候 b可以直接压入栈,栈数组变成[0 , a , b]
2:a>b的时候 b无法直接压入栈,因为那样会破坏栈的单调性我们需要将a弹出栈,然后再将b压入栈
栈数组就会变成[0 , b]
**扩展-举例:我们扩展来讲,如果原来的栈数组为[0 , 1 , 5 , 8] ,可以看出栈顶元素为8,我们需要将9压入栈的话,因为9>8,所以压入之后
栈数组变成[0 , 1 , 5 , 8 , 9]不影响原来的单调性,依然是单调递增
但是如果我们需要将4压入栈的话,我们需要先将大于4的值弹出栈,整个过程是这样的,先将要压入栈的值与当前的栈顶元素比较,即4与8比较
因为4<8,所以4无法压入栈,因此我们要弹出8,弹出8之后栈数组变成[0 , 1 , 5],然后4继续与栈顶元素5比较,因为4<5,因此我们需要弹出5,
弹出之后栈顶数组变成[0 , 1],然后4与栈顶元素1比较,因为4>1,因此4可以压入栈,压入之后栈数组变成[0 , 1 , 4]
这就是单调栈的思想,把他变成数组来讲就很好理解-单调减的栈反过来理解即可(我想我已经表达的很清楚,不懂的可以在评论里指出来,
我看到会回复解答的)

好的现在我们已经懂了单调栈的思想了,那我们开始来做题:
**题目分析:**这道题是求可以勾勒出来的最大矩形的面积,问题简化用一句话概括就是,我们要求出不同高度下的最大矩形面积,选择其中最大的面积
作为我们要输出的答案。这么来看问题已经转化成求每个高度下,对应的最大矩形面积(因为不同高度下都对应着这个高度下的一个最大矩形面积,
我们并不知道这么多个矩形面积,那个是最大的,我们只能求出来每个,然后做比较从而求出最大值==我们要输出的答案)
**针对本题的解法:**我们需要依次遍历整个数组 例如:heights = [2,1,5,6,2,3],首先我们要在数组前后分别加一个0,使得数组变成
heights = [0,2,1,5,6,2,3,0],在这里我们把这两个0成为哨兵,因为如果不加这个0,会导致遍历过后栈中可能会还存有元素,需要再加一步讨论
如果加了的话,就不用讨论了,相当于优化了过程。(可以理解成,如果最后一个3入栈之后,如何将3弹出呢?只有遇见0,0<3,如果压0入栈
才能将3弹出,这样我们才能保证我们有用的元素都弹出栈)这是加哨兵的原因。
在我们遍历数组的时候,假设我们遍历的是数组的第 i 个元素,对应的数组的值是 heights[i],我们将这个值与栈数组的栈顶元素比较,如果
大于栈顶元素,则压入栈成为新的栈顶元素,然后继续下一个,直到找到小于栈顶元素的那个值,假设这个是数组的第j个元素,然后栈顶元素为
数组的第a个元素,因为heights[a]>heights[j],因此为了不破坏单调栈的单调性,我们需要将heights[a]弹出栈数组,
那么我们可以得到数组的第a个元素的对应的最大矩形面积为heigh[a](j-a-1),因为只取中间的值,所以需要j-a-1,自己画图看看就知道了
然后我们不停的遍历数组,设一个初始值为ans = 0 ,然后 ans = max(ans , heigh[a](j-a-1)),不停的遍历即可,最终输出ans,
即为我们想要的结果。
为什么我们遇到比当前矩阵高度小的数,才需要弹出栈然后求最大矩形面积呢?
问题在于这里,因为举例,对于heights = [2,1,5,6,2,3]来说,

比如说一开始是2,如果遇到下一个是1,那么这个时候高度为2的矩阵的最大宽度就已经达到了,因为在矩阵高度一定的情况下,必然是宽度越宽
面积越大,因此我们在保证矩阵高度的情况下,遇到第一个小于我们规定的矩阵高度的值,即遇到了我们可以拓展的最大的矩阵宽度,
即为高度小于规定矩阵高度的上一个值。这样就是我们用单调栈的意义,他可以灵敏的检测到第一个小于我们规定值的值。

Python代码:

class Solution:
    def largestRectangleArea(self, heights: List[int]) -> int:
        size = len(heights) + 2  #因为要前后分别要加一个哨兵,所以数组的总长度需要加1
        heights = [0] + heights + [0]  #前后分别加一个哨兵,方便使用单调栈
        stack = [0]#设置单调栈
        ans = 0  #初始数,用来存储最大值矩阵面积
        for i in range(1 , size):  #遍历数组
            while heights[i] < heights[stack[-1]]:#如果遇见了第一个小于当前栈顶在数组中的值,则需要将栈顶所存储的数组的值的序号弹出栈
                  hights = heights[stack.pop()] #将栈顶所存储的数组的值的序号弹出栈,作为高度,这说明这个高度能够跨越的宽度已经到头
                  widths = i - stack[-1] - 1  #求出栈弹出的的序号在数组中的值作为的高度所走过的宽度,
                  #因为不包括前后两个,只包括中间的 因此需要-1
                  ans = max(ans , hights*widths)   #每个求出的面积都需要跟原来的比较,方便求出最大的面积
            stack.append(i)   #根本上述的如果遇见的值大于栈顶的存储的序号在数组中的值,那么就将这个序号压入栈,
            #继续寻找小于栈顶存储序号在数组中的值
        return ans

C++代码:

class Solution {
public:
    int largestRectangleArea(vector<int>& heights) {
        int ans = 0;  //定义一个为0的变量,用来存储矩形的面积
        heights.insert(heights.begin() , 0);  //在heights的首位插入0,因为要前后分别要加一个哨兵,所以数组的总长度需要加1
        heights.push_back(0);  //在heights的末尾插入0
        vector<int> stack = {0};  //定义一个空的栈数组
        int n = heights.size();  //求出当前数组的长度
        for (int i=1; i<n ; i++){  //开始遍历数组
            while (heights[i]<heights[stack.back()]){//如果遇见了第一个小于当前栈顶在数组中的值,则需要将栈顶所存储的数组的值的序号弹出栈
                  int highs = heights[stack.back()];//将栈顶所存储的数组的值的序号弹出栈,作为高度,这说明这个高度能够跨越的宽度已经到头
                  stack.pop_back();//将栈顶所存储的数组的值的序号弹出栈
                  int widths = i - stack.back() - 1;//求出栈弹出的的序号在数组中的值作为的高度所走过的宽度
                  //因为不包括前后两个,只包括中间的 因此需要-1
                  ans = max(ans , widths*highs);//每个求出的面积都需要跟原来的比较,方便求出最大的面积
      
            }
            stack.push_back(i);//根本上述的如果遇见的值大于栈顶的存储的序号在数组中的值,那么就将这个序号压入栈
             //继续寻找小于栈顶存储序号在数组中的值
        }
        return ans;
    }
};

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小馨馨的小翟

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值