代码随想录算法训练营day60|第十章 单调栈part03

84.柱状图中最大的矩形

代码随想录

 

今天是训练营最后一天,恭喜坚持两个月的录友们,接下来可以写一篇自己 代码随想录一刷的总结。好好回顾一下,这两个月自己的博客内容,以及自己的收获。 

找到柱状图中最大的矩形可以转换成找h*w的最大值,h可以是每一个柱子的高度,而w是构成矩形的柱子的下标差(也就是矩形的宽)。h是显而易见的,但是w比较难求,文章里面巧妙地通过找当前柱子的最近的小于当前柱子高度的柱子(这两个柱子中间的柱子的高度都大于等于当前柱子的高度,于是这个矩形的高度就确定是当前柱子的高度了),计算二者直接之间的下标差,来进而得到w的间接的方式来求的。

使用双指针法来储存暴力搜索已经搜索的值可以节省很多时间,这道题和上一题接雨水很像,但是还是在某些定义上有些不同。这道题的辅助数组储存的是每个柱子左右边第一个小于当前柱子高度的柱子的下标,而接雨水找的是左右两边的最值,最值可以通过max函数来实现,但具体到第一个小于当前柱子高度这种,那就只能再嵌套一个循环了,嵌套使用的是while循环,逻辑是从当前位置向它的左/右边去找,一直找到下标为零处/末尾处,所以必须要先初始化最初用到的值,初始化成多少也是有讲究的,最左端的下标值没有左边的元素,所以初始化下标为-1,这样不仅合乎逻辑,也能用同样的公式来推导出矩形的面积,好吧其实有点废话了。

填充完两个数组之后,再进行一次遍历,必须遍历完每个元素,这是因为每个小矩形的高度都有可能成为最大矩形的高度。

int largestRectangleArea(vector<int>& heights) {
        vector<int> minLeftIndex(heights.size());
        vector<int> minRightIndex(heights.size());
        int size = heights.size();

        // 记录每个柱子 左边第一个小于该柱子的下标
        minLeftIndex[0] = -1; // 注意这里初始化,防止下面while死循环
        for (int i = 1; i < size; i++) {
            int t = i - 1;
            // 这里不是用if,而是不断向左寻找的过程
            while (t >= 0 && heights[t] >= heights[i]) t = minLeftIndex[t];
            minLeftIndex[i] = t;
        }
        // 记录每个柱子 右边第一个小于该柱子的下标
        minRightIndex[size - 1] = size; // 注意这里初始化,防止下面while死循环
        for (int i = size - 2; i >= 0; i--) {
            int t = i + 1;
            // 这里不是用if,而是不断向右寻找的过程
            while (t < size && heights[t] >= heights[i]) t = minRightIndex[t];
            minRightIndex[i] = t;
        }
        // 求和
        int result = 0;
        for (int i = 0; i < size; i++) {
            int sum = heights[i] * (minRightIndex[i] - minLeftIndex[i] - 1);
            result = max(sum, result);
        }
        return result;
    }

单调栈的做法,也很值得考究,因为其实本质上是找左右两边第1个小于它的值,这也符合单调栈的使用逻辑,所以是可以用单调栈来做的。但是这回不同于之前的,是需要从栈底到栈顶的元素是逐渐增大的,这样在遇到一个小于栈顶元素的值的时候,就能同时也得到这个元素的左边的小于它的值的下标,于是也就是题目中所要找的左右两边第一个小于它的值。这道题还有一个需要注意的点是。需要给heights数组的头部和尾部都增加一个元素0,这样的做法其实是为了防止如果给的数组本身就是单调递增的,那么这样的话就永远也不会进入到while循环里面,所以 result是不会有任何更新的,最后的结果也仍然是0,但是实际上这个值不应该是零,它是有值的,如果你在数组末尾加了一个0的话,那么等遍历到这个0元素的时候,它势必会小于栈顶元素,所以说必然会进入到while循环里面,这样也就会更新result。而之所以在数组前面也加上一个0,是因为本身第1个高度有可能成为最大矩形的高度,如果不加,那么第1个值只能沦为它后面值的所谓左边的第1个最小值,也就是说它永远都不可能成为一个独立的高度来考量,因为它左边没有最小值,这样做让在下标为零的时候,也能符合推导最大矩形的推导公式。

int largestRectangleArea(vector<int>& heights) {
        stack<int> st;
        heights.insert(heights.begin(), 0); // 数组头部加入元素0
        heights.push_back(0); // 数组尾部加入元素0
        st.push(0);
        int result = 0;
        for (int i = 1; i < heights.size(); i++) {
            while (heights[i] < heights[st.top()]) {
                int mid = st.top();
                st.pop();
                int w = i - st.top() - 1;
                int h = heights[mid];
                result = max(result, w * h);
            }
            st.push(i);
        }
        return result;
    }

 

 

  • 7
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值