题目:柱状图中最大的矩形
给定 n 个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1 。
求在该柱状图中,能够勾勒出来的矩形的最大面积。
以上是柱状图的示例,其中每个柱子的宽度为 1,给定的高度为 [2,1,5,6,2,3]。
图中阴影部分为所能勾勒出的最大矩形面积,其面积为 10 个单位。
示例:
输入: [2,1,5,6,2,3]
输出: 10
答案:
1.暴力法
对每个子区间进行最大矩形面积求解,更新最大面积
class Solution {
public int largestRectangleArea(int[] heights) {
int maxArea = 0;
int minVal;
for(int i = 0; i < heights.length; i++) {
minVal = heights[i];
for(int j = i; j < heights.length; j++) {
if(heights[j] < minVal) minVal = heights[j];
maxArea = Math.max(maxArea,minVal*(j-i+1));
}
}
return maxArea;
}
}
时间复杂度:O(n2)。 需要枚举所有可能的柱子对。
空间复杂度:O(1) 。不需要额外的空间。
2.分治法
参考链接:https://leetcode-cn.com/problems/largest-rectangle-in-histogram/solution/zhu-zhuang-tu-zhong-zui-da-de-ju-xing-by-leetcode/
最大面积矩形存在于以下几种情况:
(1)确定了最矮柱子以后,矩形的宽尽可能往两边延伸。
(2)在最矮柱子左边的最大面积矩形(子问题)。
(3)在最矮柱子右边的最大面积矩形(子问题)。
eg:[6, 4, 5, 2, 4, 3, 9]
这里最矮柱子高度为 2 。以 2 为高的最大子矩阵面积是 2x7=14 。现在,我们考虑上面提到的第二种和第三种情况。我们对高度为 2 柱子的左边和右边采用同样的过程。在 2 的左边, 4 是最小的,形成区域为 4x3=12 。将左边区域再继续分,矩形的面积分别为 6x1=6 和 5x1=5 。同样的,我们可以求出右边区域的面积为 3x3=9, 4x1=4 和 9x1=9 。因此,我们得到最大面积是 14 。具体过程可参考下图:
class Solution {
public int largestRectangleArea(int[] heights) {
return calculateArea(heights, 0, heights.length - 1);
}
public int calculateArea(int[] heights, int start, int end) {
if (start > end)
return 0;
int minindex = start;
for (int i = start; i <= end; i++)
if (heights[minindex] > heights[i])
minindex = i;
return Math.max(heights[minindex] * (end - start + 1), Math.max(calculateArea(heights, start, minindex - 1), calculateArea(heights, minindex + 1, end)));
}
}
时间复杂度:
平均开销:O(nlogn)
最坏情况:O(n2)。如果数组中的数字是有序的,分治算法将没有任何优化效果。
空间复杂度:O(n)。最坏情况下递归需要 O(n) 的空间。
3.单调栈(想法很重要)
我们维护一个栈。一开始,我们把 -1 放进栈的顶部来表示开始。初始化时,按照从左到右的顺序,我们不断将柱子的序号放进栈中,直到遇到相邻柱子呈下降关系,也就是 a[i-1] > a[i] 。将栈中的序号弹出,直到遇到 stack[j] 满足a[stack[j]]≤a[i](保持单调递增的关系)。每次弹出stack[top] 时,记当前元素在原数组中的下标为 i ,当前弹出元素为高的最大矩形面积为:
(i−stack[top−1]−1)×a[stack[top]].
public class Solution {
public int largestRectangleArea(int[] heights) {
Stack < Integer > stack = new Stack < > ();
stack.push(-1);
int maxarea = 0;
for (int i = 0; i < heights.length; ++i) {
while (stack.peek() != -1 && heights[stack.peek()] >= heights[i])
maxarea = Math.max(maxarea, heights[stack.pop()] * (i - stack.peek() - 1));
stack.push(i);
}
while (stack.peek() != -1)
maxarea = Math.max(maxarea, heights[stack.pop()] * (heights.length - stack.peek() -1));
return maxarea;
}
}
时间复杂度:O(n)。 n 个数字每个会被压栈弹栈各一次。
空间复杂度:O(n)。用来存放栈中元素。