题目描述
// 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;
}
}