leetcode84 柱状图中最大的矩形

今天要讲的是leetcode84题,柱状图中最大的矩形,题目链接:https://leetcode-cn.com/problems/largest-rectangle-in-histogram/

题目大意:给你一个数组,数组中的每个数都对应的是高度,你把他想象成一个柱状图,每个柱子的宽度都是1,你来求他形成的最大矩形的面积,就以示例来说,给你的数组为[2,1,5,6,2,3],你可以想象成这个样子
leetcode84 柱状图中最大的矩形_第1张图片
很明显,矩形的最大面积是这样的

leetcode84 柱状图中最大的矩形_第2张图片
你可以列举其他的情况,但是会发现都不如这个大,矩形的面积是高乘宽,所以我们要求的面积不仅只是和高度有关,还和宽度有关,即使heights[3]=6比heights[2]=5要来得大,但是由于5的宽度可以向右扩展到6这边,所以5乘上2要大于6乘上1,因此最大的面积是以heights[2]为高的。

说到这里,我们就可以想到可以以每个heights[i]为高一一列举,看看他们能到达的最大左边界和右边界,就是这个宽,然后再乘以高度就得到了面积,最后取最大值就可以得出答案。就比如heights[2]它最远的左边界就是2他自己,因为它左边的数[2,1]都比他小,因为我们不能缺胳膊断腿,这样就不是矩形了,而右边界可以达到3,因为6比5大,我们可以只取6中的5高度来使得我们的宽度变大。简单的来说,就是我们以一个heights[i]为高度中心,来找出左边和右边第一个比他小的边界在哪里。

先来看代码:

class Solution {
     
public:
    int largestRectangleArea(vector<int>& heights) {
     
        int n=heights.size();
        if(n==0)
            return 0;
        int left[n];
        int right[n];
        stack<int> l;
        for(int i=0;i<n;i++){
     
            while(!l.empty()&&heights[i]<=heights[l.top()])
                l.pop();
            if(l.empty())
                left[i]=-1;
            else
                left[i]=l.top();
            l.push(i);
        }
        stack<int> r;
        for(int i=n-1;i>=0;i--){
     
            while(!r.empty()&&heights[i]<=heights[r.top()])
                r.pop();
            if(r.empty())
                right[i]=n;
            else
                right[i]=r.top();
            r.push(i);
        }
        int ret=0;
        for(int i=0;i<n;i++){
     
            ret=max(ret,heights[i]*(right[i]-left[i]-1));
        }
        return ret;
    }
};

这里用到的数据结构还是单调栈,使用的算法思想就是预处理,这个预处理比较简单,只要处理完成就可以直接得出答案。

我们left数组就是以i为高度左边最远能到达的下标,例如[-1,1,1],对于下标2来说,我们左边最远能到达1这个下标,注意我这边说的左边最远到达和右边最远到达都是左开右开的开区间,就比如一个下标 i 左边可以到达-1,右边可以到达4,所以它的范围应该是(-1,4),并不是闭区间,所以最后求答案的时候,它的宽度应该是右边界-左边界-1,即4-(-1)-1=4.

之所以使用单调栈,就是因为每个高度的左边和右边都有一个瓶颈,例如[3,2,4,5],对于4这个高度来说,它左边的瓶颈就是2,因为2<4,我们单调栈中从栈顶到栈底的元素应该是递减的,这是因为如果2不是左边的瓶颈的话,那么3就一定不是左边的瓶颈,因为3>2。

有意思的是,我们存入栈的元素是下标,而比较的时候是用heights这个数组来进行比较,这个地方比较巧妙。

我们来模拟一下这个过程:还是题目给我们的例子[2,1,5,6,2,3],我就单单以左边界举例
1)i=0,我们的栈为空,所以0入栈,left[0]=-1,意思就是2这个高度,它能到达的最远左边界为-1;
2)i=1,1<2所以0出栈,left[0]=-1,1入栈,此时栈中元素只有1,千万不要给绕晕,栈中的元素是下标
3)i=2,5>1,left[2]=1,2入栈,栈中元素[1,2];
4)i=3,6>5, left[3]=2,3入栈,栈中元素[1,2,3];
5)i=4,2<6,3出栈,2<5,2出栈,left[4]=1,4入栈,栈中元素[1,4];
6)i=5,3>2,left[5]=4,5入栈;

至此,我们就已经预处理完left这个数组,这样就可以知道任意一个高度它左边界是哪儿了,同样的方法我们可以完成right这个数组,区别就是我们需要从右往左遍历。

还有一个值得注意的地方是,就算当前的高度和栈顶的高度一样高,我们照样还是需要将栈顶的元素pop出来,举个例子:[2,2,3],对于3来说,它左边的瓶颈就是2,如果我们不将栈顶的元素进行更新的话那么栈顶保留的下标是0,换句话说,就是3的瓶颈不是下标为1的2,而是下标为0的2,这样求出来的面积是((3-0)-1)*3=6,这显然是不符合这个事实的。

好啦,至此这道题的细节我都讲完啦,喜欢的可以点个赞哦。
继续加油:)

你可能感兴趣的:(leetcode,算法,数据结构)