【LeetCode】 84. Largest Rectangle in Histogram

84. Largest Rectangle in Histogram

介绍

Given n non-negative integers representing the histogram’s bar height where the width of each bar is 1, find the area of largest rectangle in the histogram.

For example,
Given heights = [2,1,5,6,2,3],
return 10.

题意,寻找柱型图中最大的矩阵面积值。

解答

这是LeetCode上的一道经典题目,难度也比较大。自己思考了半天也没找到合适的解法,参考下网上的解法,做个记录。

方法1

暴力破解:

这个方法应该每个人都能够想到,但是估计也是一眼就能排除的答案。

穷举任意两条边,然后找到这两条边中的最小高度,组成矩形,然后计算矩形面积,取最大值就行了。

任意两条边的确定就是任意两个子数组,时间复杂度为O(n^2),查找两条边之间的最小高度是O(n)。时间复杂度为O(n^3)。

方法二:

我们直到,对于得到的最大的矩形面积,它肯定是以某个下标上的高度为高度,然后往两边扩展一定宽度得到的。

所以,我们只需要以数组中的每一个下标上的高度为基准,求出相应的矩形面积。计算得到最大值就可以了。

时间复杂度为O(n^2)。

方法三

这个方法才是本篇文章的重点。

在看这个方法的时候,我找了好几篇文章才看懂。感觉算法这个东西就是就算自己懂了,也很难和不懂的人讲清楚,自己不懂,只听别人讲也很难明白。所以推荐大家还是多按照算法步骤自己一步一步来计算一下,就很容易就能理解了。

这个解法的核心在于用栈来保存一个高度依次递增的序列。

对于这样一个高度序列[1,2,3,4,5]。如果我们想要计算矩阵面积的最大值。因该是1*5 vs 2*4 vs 3*3 vs 4*2 vs 5*1。这几个值进行比较。

但是给定的序列,显然并不全都是有序增长的。如对于示例序列,[2,1,5,6,2,3]就不是有序增长的。

那么关键就是我们遇到了一个逆序序列,应该怎样处理这个逆序序列呢?

对于一个栈,如果栈为空,或者当前下标的高度大于栈顶的值,我们就将这个高度值压入栈中。

如果当前下标的高度小于栈顶的值,我们就将弹出栈顶元素的值,然后以栈顶元素的值为高度,连续弹出的次数为宽度,计算相应的矩形面积。然后我们将弹出的高度的值都换成这个当前下标的值,并且当前下标的值自身也需要压入。

下面是操作的模拟过程:

res为最大的矩阵大小,初始值为0,。

  1. 下标为0时,遇到高度值为2,此时栈为空,直接压入栈。
  2. 下标为1时,遇到高度值为1,此时小于栈顶元素,弹出2,计算相应的矩阵面积为 2*1。res = 2。然后将栈中弹出的元素的值换为1压入。再压入1。此时栈内值为1,1
  3. 对于下标2,3,都大于栈顶元素,直接压入。此时栈内值为1,1,5,6
  4. 对于下标值为4,元素值为2,需要连读弹出6和5,弹出过程计算相应的矩形面积6*1,和5*2,得到res = 10。然后将元素换为2,压入。此时栈内值为1,1,2,2,2
  5. 对于下标5,压入高度3。此时栈内值为1,1,2,2,2,3
  6. 最后遍历结束后,栈中就是一个增序数列,然后计算它的矩形面积。然后不断的旧的res比较,最终得到结果值。

我们来说下,为什么这个算法能够得到正确值。

其实很简单,对于栈,我们遍历结束之后,他内就存储着当前这个结点到达尾部时候,矩形的高度的值。

如最后栈内值为1,1,2,2,2,3。就代表了最后一个单位宽度的矩形的高度是3,最后两个单位宽度的矩形的高度是2。

可以参考下图形看看是不是这个样子。

那么除此之外,如果矩形的最后一条边不是尾部呢?

还记的我们遇到逆序时候的处理吗?如果遇到了逆序,我们需要将栈中元素不断弹出,然后计算矩形面积,直到遇到小于当前高度的值,并将弹出的值都换为当前高度重新压入。

所以在这个时候,相应矩阵的值,我们已经计算过了。

因为我们得到的结果就一定是最终的结果。

class Solution {
public:
 int largestRectangleArea(vector<int>& heights) {
        stack<int> help;
        int res = 0;
        for(int i = 0; i < heights.size(); ++i)
        {
            int count = 0;
            while(!help.empty() && help.top() > heights[i])
            {
                int h = help.top();
                help.pop(); 
                ++count;
                res = max( res,h*count);

            }
            while(count-- > 0)
                help.push(heights[i]);
            help.push(heights[i]);
        }
        int count = 0;
        while(!help.empty())
        {
            int h = help.top();
            help.pop(); 
            ++count;
            res = max( res,h*count);
        }
        return res;
    }
};

我们可以在高度序列中添加一个最小值,作为哨兵,它将代替上述代码中最后检查元素是否为空,并计算矩阵面积的操作。

class Solution {
public:
    int largestRectangleArea(vector<int>& heights) {
        stack<int> help;
        heights.push_back(-1);
        int res = 0;
        for(int i = 0; i < heights.size(); ++i)
        {
            int count = 0;
            while(!help.empty() && help.top() > heights[i])
            {
                int h = help.top();
                help.pop(); 
                ++count;
                res = max( res,h*count);

            }
            while(count-- > 0)
                help.push(heights[i]);
            help.push(heights[i]);
        }
        heights.pop_back();
        return res;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值