【每日一题】LeetCode. 84. 柱状图中最大的矩形

每日一题,防止痴呆 = =

一、题目大意

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

求在该柱状图中,能够勾勒出来的矩形的最大面积。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/largest-rectangle-in-histogram

二、题目思路以及AC代码

这道题被之前写的一道题迷惑思路了。
https://leetcode-cn.com/problems/container-with-most-water/,没错,就是这道题,然后我就想了半天双指针的解法,看看数学上是不是有什么规律可以排除一些搜索选项,然而,失败了 = =。

思路:单调栈

这道题和上面我提到的那道题区别是,这道题的面积计算方式是:区间最小值×区间长度,上面我提到的那道题的计算方式是:区间边界较小值×区间长度。

我这道题一开始是用的暴力法,即遍历所有的区间,计算比较即可,时间复杂度是O(n^2),c++超时了。然后参照题解写出了单调栈的代码。思路是这样的,对于这道题,我们的常规思路是,可以遍历每个柱子的高度,然后去找到该柱子两侧高度不小于该柱子高度的最远左右边界。其实换一种想法,也可以是找到该柱子两侧最近的高度小于该柱子高度的左右边界,无非就差一个嘛。

那么简单的遍历的话,则还是O(n^2)的复杂度。这里我们可以考虑一个性质,对于第i个柱子,其高度为heights[i],其左侧有若干柱子,其中heights[j] > heights[k],且j < k,那么第i个柱子左侧最近的小于其高度的柱子肯定不会是j,因为k把它拦住了。这样的话,我们就可以利用单调栈,来求解数组中任意一个位置处的两侧高度小于该位置柱子的左右边界。

关于单调栈的理论这里就不说了,具体可以举个例子,就拿样例来说吧。
对于[2, 1, 5, 6, 2, 3],我们给出求解各个位置左边界的流程,右边界类似。

首先,栈为空,2(0)的左边界为哨兵 -1,2(0)进栈(其中2表示值,0表示索引位置)
比较 2 >= 1,则2(0)出栈,此时栈为空,1(1)的左边界为哨兵 -1,1(0)进栈
比较 1 < 5,则5(2)的左边界为此时的栈顶元素索引 0,5(2)进栈
比较 5 < 6,则6(3)的左边界为此时的栈顶元素索引 2,6(3)进栈
比较 6 >= 2,5 >= 2,1 < 2则6(3),5(2)出栈,则2(4)的左边界为此时的栈顶元素索引 1,2(4)进栈
比较 2 < 3,则3(5)的左边界为此时的栈顶元素索引 4,3(5)进栈,
所有柱子的左边界计算完毕,同理去计算右边界,然后利用左右边界计算结果取最大值即可。

优化

这里可以进行一个简单的优化,因为我们在遍历的时候,当一个元素要出栈的时候,则此时的遍历索引正好是其右边界,但是有些许的不同,因为我们上述要求的右边界是第一个小于高度的右边界,而这里是小于等于,但其实没有关系,我们就差在一个等于嘛,虽然在等于的情况下,前面几个的右边界是错误的(而且得到的结果会比正确的小),但是最右边的那个元素的右边界一定是正确的,又由于左边界的计算方式没有变,所以还是会得到正确的结果。

AC代码

这里写出了优化之后的AC代码:

class Solution {
public:
    int largestRectangleArea(vector<int>& heights) {
        int n_size = heights.size();
        if (!n_size) return 0;

        stack<int> mono_stack;
        int right[n_size], left[n_size];
        for (int i=0;i<n_size;i++) {
            right[i] = n_size;
        }
        for (int i=0;i<n_size;i++) {
            while (!mono_stack.empty() && heights[mono_stack.top()] >= heights[i]) {
                right[mono_stack.top()] = i;
                cout << mono_stack.top() << "-->" << i << endl;
                mono_stack.pop();
            }
            if (mono_stack.empty()) left[i] = -1;
            else left[i] = mono_stack.top();
            mono_stack.push(i);
        }

        int ans = -1;
        for (int i=0;i<n_size;i++) {
            ans = max(ans, (right[i] - left[i] - 1) * heights[i]);
        }

        return ans;
    }
};

如果有问题,欢迎大家指正!!!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值