84.柱状图中最大的矩形
题目
给定 n 个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1 。
求在该柱状图中,能够勾勒出来的矩形的最大面积。
单调栈(单调递减)
思路
mid作为中间值的条件,以 mid 左边最小的值为 left ,以 mid 右边最小的值为 right ,减去 left 和 right 两边的值,取 left 和 right 中间所包含的最大面积。
- 栈的执行过程:
- 当遍历的值比栈口元素的值小时,获取 left , right , mid 的值
- 当遍历的值比栈口元素的值大时,将其压栈
- 为何将数组首尾添加 0 ?
-
当数组元素形成递增的值时,如:[1, 2, 3, 4],压栈之后形成:[4, 3, 2, 1]每个遍历的元素都比栈口的元素大,则不能寻找到最小的值,在末尾添加 0 ,可以在执行到 0 时,获取 left , right , mid 的值
-
当数组元素形成递减的值时,如:[4, 3, 2, 1],压栈之后形成:[1, 2, 3, 4]每个遍历的元素都比栈口的元素小,则执行程序,但在获取 left , right , mid 的值时,则无法获取,当栈口为 4 ,遍历元素为 3 ,那么 mid = 4 , right = 3 , left = ? ,所以在数组头部加上 0 ,能让 left 获取到值
-
class Solution {
public int largestRectangleArea(int[] heights) {
// 计算最终结果
int res = 0;
// +2是为了在数组首尾加0,方便之后的操作
int[] newHeights = new int[heights.length + 2];
newHeights[0] = 0;
newHeights[newHeights.length - 1] = 0;
// 将heights数组中元素加入到newHeights数组,从下标为1开始
for(int i = 0; i < heights.length; i++){
newHeights[i + 1] = heights[i];
}
// 将heights数组替代为newHeights数组
heights = newHeights;
Stack<Integer> stack = new Stack<>();
// 将下标0的元素压栈
stack.push(0);
// 下标0已压栈,从下标1开始
for(int i = 1; i < heights.length; i++){
// 单调递减,所以heights元素的值比栈中的值大时,压栈
if(heights[i] > heights[stack.peek()]){
stack.push(i);
// heights元素的值与栈中的值相等时,也就是有了重复值,可以选择弹出再压栈,也可以直接压栈
}else if(heights[i] == heights[stack.peek()]){
stack.pop(); // 加不加都可以
stack.push(i);
// heights元素的值比栈中的值小时,符合单调递减的情况
}else{
while(!stack.isEmpty() && heights[i] < heights[stack.peek()]){
// 获取当时的栈顶元素
int middle = stack.peek();
// 将其弹出
stack.pop();
// 获取下一个栈顶元素
int left = stack.peek();
// 标记已经遍历到的位置
int right = i;
// 计算宽度
int w = right - left - 1;
// 计算高度
int h = heights[middle];
res = Math.max(res, w * h);
}
stack.push(i);
}
}
return res;
}
}
单调栈(精简)
class Solution {
public int largestRectangleArea(int[] heights) {
int res = 0;
int[] newHeights = new int[heights.length + 2];
newHeights[0] = 0;
newHeights[newHeights.length - 1] = 0;
/*
数组扩容方法:System.arraycopy();需要5个参数:
1. 拷贝源(要拷贝的数组)
2. 拷贝源中需要从哪拷贝的下标位置
3. 目标源(拷贝到哪里的数组)
4. 目标源中需要放置在哪个位置的下标位置
5. 需要拷贝的长度
*/
System.arraycopy(heights, 0, newHeights, 1, heights.length);
Stack<Integer> stack = new Stack<>();
stack.push(0);
for(int i = 1; i < newHeights.length; i++){
while(newHeights[i] < newHeights[stack.peek()]){
int middle = stack.pop();
int w = i - stack.peek() - 1;
int h = newHeights[middle];
res = Math.max(res, w * h);
}
stack.push(i);
}
return res;
}
}