Leetcode84. 柱状图中的最大的矩形

434 阅读3分钟

要求:

给定 n 个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1 。 求在该柱状图中,能够勾勒出来的矩形的最大面积。

示例:

输入: [2,1,5,6,2,3]
输出: 10

思路:

暴力解法:

  1. 往左边找,找到大于等于当前柱形高度的最左边元素的下标。
  2. 往右边找,找到大于等于当前柱形高度的最右边元素的下标。
  3. 转自https://leetcode-cn.com/problems/largest-rectangle-in-histogram/solution/bao-li-jie-fa-zhan-by-liweiwei1419/

对于每个位置都这样判断,一开始再进行一些特殊的判定,最后记录最大值就行。

public class Solution {

    public int largestRectangleArea(int[] heights) {
        int len = heights.length;
        // 特判
        if (len == 0) {
            return 0;
        }

        int res = 0;
        for (int i = 0; i < len; i++) {

            // 找左边最后 1 个大于等于 heights[i] 的下标
            int left = i;
            int curHeight = heights[i];
            while (left > 0 && heights[left - 1] >= curHeight) {
                left--;
            }

            // 找右边最后 1 个大于等于 heights[i] 的索引
            int right = i;
            while (right < len - 1 && heights[right + 1] >= curHeight) {
                right++;
            }

            int width = right - left + 1;
            res = Math.max(res, width * curHeight);
        }
        return res;
    }
}

利用单调栈的做法: 按顺序遍历数组高度,例如以[2, 1, 5, 6, 2, 3]为例。

  1. 对于2,往右遍历一位,1就比2小,所以以2为高度的矩形的右边界就已经限定了,但是2的左边界还不确定。为了可以确定左边界,我们添加一个0先进去,称为左侧哨兵。值只需要比数组中任意一个元素小即可。这样2的矩形面积就确定了。
  2. 对于1,左侧边界确定了,就是左侧哨兵,因为2比1大,所以2无法封锁1的左侧边界,1这时候需要寻找他的右侧边界。5比1大,6比1大,2比1大,3比1大,所以以1为高度的矩形的宽是整个数组的长度。
  3. 依次类推,遇见比当前元素大的,都是矩形宽度加一,遇见比当前元素小的,说明要跳出来计算边界。
  4. 以上的步骤和暴力解法并无差异,妙就妙在使用栈对元素进行存储,前后还加了两个哨兵,对元素的边界进行限界。

**[注意:]**在缓存数据的时候,从左向右缓存,在计算结果的时候是从右往左计算,而且计算完成之后不需要再进栈,符合后进先出的特点。

  1. 每次进栈的是索引,元素在数组中的索引。
  2. 新建一个数组存储元素,需要一头一尾加两个哨兵元素,中间的元素全部由原数组复制进来。
  3. 比较的时候不需要弹出,调用peek()即可,读取元素值的时候需要将元素弹出,调用poll()。

代码:

class Solution {
    public int largestRectangleArea(int[] heights) {
        //单调栈
        int len = heights.length;
        if (len == 0) return 0;
        if (len == 1) return heights[0];
        int res = 0;
        int[] nums = new int[len+2];
        nums[0] = 0;
        System.arraycopy(heights, 0, nums, 1, len);
        nums[len+1] = 0;
        len = len + 2;
        heights = nums;
        Deque<Integre> stack = new ArrayDeque<>(len);
        stack.addLast(0);
        
        for (int i = 0; i < len: i++) {
            while (heights[i] < heights[stack.peekLast()]) {
                int cur = heights[stack.pollLast()];
                int wid = i - stack.peekLast() - 1;
                res = Math.max(res, cur * wid);
            }
            stack.addLast(i);
        }
        return res;
    }
}