「这是我参与2022首次更文挑战的第30天,活动详情查看:2022首次更文挑战」。
题目:给定长度为n的非负数组,数组中元素表示柱状图中各个柱子的高度,每个柱子彼此相邻,并且宽度为1。求由这些柱子勾勒出矩形的最大的面积。
解题思路
本题和之前接雨水的思路类似,接雨水是找到当前柱子的左边界和右边界,之后利用左右边界较小的那个减去当前柱子的高度即为可装雨水的量。对于本题来说,每个柱子的最大的矩形面积取决于由当前柱子的左右边界组成的范围(左右边界柱子高度都大于当前柱子才可作为边界),此时我们只需计算每个柱子的左右边界即可,之后当前柱子组成的最大矩形面积即为左右边界距离乘以当前柱子高度,代码如下:
public int largestRectangleArea(int[] heights) {
if(heights.length==0) return 0;
int maxArea=0;
for(int i=0;i<heights.length;i++){
int left=i;
int right=i;
while(left>0&&heights[left-1]>=heights[i]){
left--;
}
while(right<heights.length-1&&heights[right+1]>=heights[i]){
right++;
}
maxArea = Math.max(maxArea, heights[i]*(right-left+1));
}
return maxArea;
}
上述代码属于暴力求解,时间复杂度为, 空间复杂度为。官方测试样例超时无法通过。
单调栈
我们想象一下,对于柱子数组[3, 1, 2],首先遍历数组,第一个柱子高度为3,此时该柱子所能组成的最大面积取决于下一个柱子的高度,而下一个柱子的高度小于3,因此该柱子最大面积就为3*1,之后第二个柱子1,其最大面积取决于左右元素,左右元素均小于当前柱子,则此柱子面积为1*3,2同理。此时我们可以用单调栈来解决问题,栈中的元素是递增的,如果当前柱子高度大于栈顶柱子的高度,则入栈,否则栈顶的柱子就出栈计算面积。但此时面临一个问题,如果元素全部都是递增的,那数组不就不知道在什么时候计算面积了?如果数组前面全部元素都出栈了,例如[6, 7, 8, 2, 1],循环到2则[6, 7, 8 ]出栈,但到1的时候计算的面积只有2->1的面积,而实际面积为前面的宽度乘以当前柱子高度,因此在出栈的同时保留前面的宽度?
解决方法就是在数组的首尾都新增0元素,此时就可以在保留已出栈元素数量且保证柱子一直递增也计算面积,代码如下:
public int largestRectangleArea2(int[] heights) {
int[] heights_copy = new int[heights.length + 2];
System.arraycopy(heights, 0, heights_copy, 1, heights.length);
int maxArea=0;
Deque<Integer> stack = new ArrayDeque<>();
for(int i=0;i<heights_copy.length;i++){
while(!stack.isEmpty()&&heights_copy[i]<heights_copy[stack.peek()]){
int index = stack.pop();
maxArea = Math.max(maxArea, heights_copy[index]*(i-index));
}
stack.push(i);
}
return maxArea;
}
依次出栈的柱子高度必定小于后面的柱子高度,因此可以这样计算!(出栈的条件是当前柱子小于栈顶的柱子)。 上述代码时间复杂度为,空间复杂度为,注意:上述代码将栈改为双端队列,时间耗费明显减少几倍。为什么推荐使用Deque而不使用栈,如下文: JAVA 栈,为什么要使用Deque,而不推荐使用Stack,Deque中ArrayDeque与LinkedList的区别,Deque方法详解