【Leetcode】84. 柱状图中最大的矩形

45 阅读1分钟

题目描述

在这里插入图片描述

// 84. 柱状图中最大的矩形

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

// 求在该柱状图中,能够勾勒出来的矩形的最大面积。

题解

题解难主要难在思路

// 暴力法
// 超出时间限制,没意义
class Solution {
    public int largestRectangleArea(int[] heights) {
        int res = 0;
        for (int i = 0; i < heights.length; i++) {
            int minheight = heights[i];
            for (int j = i; j < heights.length; j++) {
                int temp = Math.min(heights[i], heights[j]);
                minheight = Math.min(minheight, temp);
                res = Math.max(res, minheight * (j - i + 1));
            }
        }
        return res;
    }
}





// 单调栈法
// 
// 我们在遍历数组的时候,for循环遍历元素(柱子高度)为heights[i],在循环内,
// 反复找以heights[i]为高(纵向为高,横向为宽)的矩形最大面积,
// 用来更新res,最后返回res,就做完了。
//
// 我们需要找到以heights[i]为高的矩形的最大面积,就需要再确定这个矩形
// 左右边界,来找到矩形的宽。左右边界怎么找?
// 实际就是heights[i]出发从左边数,第一个比heights[i]小的柱子,就是左边界,
// heights[i]出发从右边数,第一个比heights[i]小的柱子,就是右边界。
//
// 所谓单调栈,就是栈顶到栈底的数具有某种单调性(单调递增/单调递减),
// 越靠近栈顶的元素越后进栈。由于我们需要找遍历高度的左边界,左边界一定是比
// 当前高度要低才能称之为边界,所以我们要维护一个单调递减栈,栈顶始终要大。
//
// 有了单调栈之后,我们改变了实现策略,将想要的矩形高度存在单调栈顶,左边界
// 存在单调栈的栈顶之下,用指针i去遍历矩形高的右边界。这是本解法能够只使用一个
// for循环解决的原因。


// System.arraycopy()函数复制heights数组,arraycopy(Object src, int srcPos,
// Object dest, int destPos, int length) src:源数组;srcPos:源数组要复制的起始位置;
// dest:目的数组;destPos:目的数组放置的起始位置;length:复制的长度。
// 相当于在heights数组的头和尾各加一个0。
// 
// 构建单调栈stack,for循环遍历temp,索引为i,元素为temp[i],
// 如果stack为空,或者遍历的元素temp[i]大于等于栈顶索引对应元素temp[stack.peek()],
// 则直接将i压入stack,此时栈顶元素就是当前遍历元素temp[i],栈顶往下数第二个栈元素,
// 就是数组heights从当前遍历元素temp[i]往左数的第一个比当前遍历元素小的柱子,
// 就是我们要的左边界。
// 
// 如果stack非空,并且遍历的元素temp[i]小于栈顶索引对应元素temp[stack.peek()],
// 说明temp[i]是stack.peek()索引在右边的第一个小于stack.peek()索引的柱子,
// 就是我们要的右边界。将栈顶索引弹出取得对应元素temp[stack.pop()]记为h
// 这就是我们的高。刚刚说过,原来的栈顶往下数第二个栈元素就是我们要的左边界,
// 现在原来的栈顶被弹出了,左边界成为了新的栈顶,所以右边界i减去左边界
// stack.peek()再减1就是当前以h元素为高,左右边界夹起来的范围为宽的矩形的面积。
// (可以在res后面打印出来看看,就更理解了)
// System.out.println("stack.peek(): " + stack.peek() + " h: " + h + " i: " + i + " res: " + res);
// temp: [0,2,1,5,6,2,3,0]
//        0 1 2 3 4 5 6 7
// 比如在h取到5的时候,此时左边界柱子索引为2,高度为1,右边界柱子索引为5,
// 高度为2。这时以h为高的最大矩形在哪?就在高度5的柱子和高度6的柱子这个范围,
// 边界位置短于h,所以并不能取到,所以是i-stack.peek()-1为长。
// 
// 用这个面积循环更新res,最后返回res,题目就做完了。
// 
// 执行用时:40 ms, 在所有 Java 提交中击败了30.41%的用户
// 内存消耗:50.4 MB, 在所有 Java 提交中击败了38.80%的用户
class Solution {
    public int largestRectangleArea(int[] heights) {
		int[] temp = new int[heights.length + 2];
		System.arraycopy(heights, 0, temp, 1, heights.length);
		
		Stack<Integer> stack = new Stack<>();
		int res = 0;
		for (int i = 0; i < temp.length; i++) {
			while (!stack.isEmpty() && temp[i] < temp[stack.peek()]) {
				int h = temp[stack.pop()];
				res = Math.max(res, (i - stack.peek() - 1) * h);
			}
			stack.push(i);
		}
		return res;
    }
}