【问题描述】[困难]
给定 n 个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1 。求在该柱状图中,能够勾勒出来的矩形的最大面积。以上是柱状图的示例,其中每个柱子的宽度为 1,给定的高度为 [2,1,5,6,2,3]。图中阴影部分为所能勾勒出的最大矩形面积,其面积为 10 个单位。输入: [2,1,5,6,2,3]
输出: 10来源:力扣(LeetCode)
【解答思路】
1. 暴力
可以枚举以每个柱形为高度的最大矩形的面积。
具体来说就是:依次遍历柱形的高度,对于每一个高度分别向两边扩散,求出以当前高度为矩形的最大宽度多少。
时间复杂度:O(N^2) 空间复杂度:O(1)
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. 单调栈
时间复杂度:O(N) 空间复杂度:O(N)
import java.util.ArrayDeque;
import java.util.Deque;public class Solution {public int largestRectangleArea(int[] heights) {int len = heights.length;if (len == 0) {return 0;}if (len == 1) {return heights[0];}int area = 0;Deque<Integer> stack = new ArrayDeque<>();for (int i = 0; i < len; i++) {while (!stack.isEmpty() && heights[stack.peekLast()] > heights[i]){int height = heights[stack.removeLast()];while (!stack.isEmpty() && heights[stack.peekLast()] == height){stack.removeLast();}int width;if (stack.isEmpty()){//iwidth = i;} else {width = i - stack.peekLast() - 1;}area = Math.max(area , width * height);}stack.addLast(i);}
//遍历完还没出栈的while (!stack.isEmpty()){int height = heights[stack.removeLast()];while (!stack.isEmpty() && heights[stack.peekLast()] == height){stack.removeLast();}int width;if (stack.isEmpty()){width = len;//肯定能到达} else {width = len - stack.peekLast() - 1;}area = Math.max(area , width * height);}return area;}
}
3. 哨兵思想+单调栈
时间复杂度:O(N) 空间复杂度:O(N)
import java.util.ArrayDeque;
import java.util.Deque;public class Solution {public int largestRectangleArea(int[] heights) {int len = heights.length;if (len == 0) {return 0;}if (len == 1) {return heights[0];}int area = 0;int[] newHeights = new int[len + 2];for (int i = 0; i < len; i++) {newHeights[i + 1] = heights[i];}len += 2;heights = newHeights;Deque<Integer> stack = new ArrayDeque<>();stack.addLast(0);for (int i = 1; i < len; i++) {while (heights[stack.peekLast()] > heights[i]) {int height = heights[stack.removeLast()];int width = i - stack.peekLast() - 1;area = Math.max(area, width * height);}stack.addLast(i);}return area;}
}
【总结】
1.算法思想
其实可以把这个想象成锯木板,如果木板都是递增的那我很开心,如果突然遇到一块木板i矮了一截,那我就先找之前最戳出来的一块(其实就是第i-1块),计算一下这个木板单独的面积,然后把它锯成次高的,这是因为我之后的计算都再也用不着这块木板本身的高度了。再然后如果发觉次高的仍然比现在这个i木板高,那我继续单独计算这个次高木板的面积(应该是第i-1和i-2块),再把它俩锯短。直到发觉不需要锯就比第i块矮了,那我继续开开心心往右找更高的。当然为了避免到了最后一直都是递增的,所以可以在最后加一块高度为0的木板。
这个算法的关键点是把那些戳出来的木板早点单独拎出来计算,然后就用不着这个值了。
2.对于 使用 Java 的朋友, Stack 改用 Deque 的问题,参考文章:https://mp.weixin.qq.com/s/Ba8jrULf8NJbENK6WGrVWg
3.单调栈
出栈都要做的判断!stack.isEmpty()
4.哨兵思想
转载链接:https://leetcode-cn.com/problems/largest-rectangle-in-histogram/solution/bao-li-jie-fa-zhan-by-liweiwei1419/
参考链接:
https://leetcode-cn.com/problems/largest-rectangle-in-histogram/solution/xiang-jie-dan-diao-zhan-bi-xu-miao-dong-by-sweetie/