栈&分治 84. 柱状图中最大矩形
给定 n 个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1 。
求在该柱状图中,能够勾勒出来的矩形的最大面积。
以上是柱状图的示例,其中每个柱子的宽度为 1,给定的高度为
2,1,5,6,2,3]
图中阴影部分为所能勾勒出的最大矩形面积,其面积为
10
个单位。示例:
输入: [2,1,5,6,2,3] 输出: 10
一. 实现思路
1. 暴力解
循环每个元素,查询这个元素前面的数或者后面的数是否比他大:getBefore
和 getAfter
如果大,接着往下找,如果小就说明往前往后无法在拼接成完整的矩形
这个解法比较好理解,但是效率非常低
2. 单调栈
在这种方法中,我们维护一个栈。
- 把 -1 放进栈的顶部来表示开始。
- 初始化时,按照从左到右的顺序,我们不断将柱子的序号放进栈中,直到遇到相邻柱子呈下降关系,也就是
a[i-1] > a[i]
。 - 然后将栈中的序号弹出,直到遇到
stack[j]
满足a[stack[j]]<=a[i]
。 - 每次我们弹出下标时,我们用弹出元素作为高形成的最大面积矩形的宽是
当前元素
与stack[top-1]
之间的那些柱子。也就是当我们弹出stack[top]
时,记当前元素在原数组中的下标为i
,当前弹出元素为高的最大矩形面积为:(i−stack[top−1]−1)×a[stack[top]]
- 更进一步,当我们到达数组的尾部时,我们将栈中剩余元素全部弹出栈。在弹出每一个元素是,我们用下面的式子来求面积:
(stack[top]−stack[top−1])×a[stack[top]]
,其中,stack[top]
表示刚刚被弹出的元素。因此,我们可以通过每次比较新计算的矩形面积来获得最大的矩形面积。
3. 判断极端情况的分治
不断拆分找最小
极端情况:判断了递增和递减两种情况
二.代码实现
1. 暴力解
class Solution {
public int largestRectangleArea(int[] heights) {
if (heights.length==0){
return 0;
}
int[] xl = new int[heights.length];
List<Integer> integers = Arrays.stream(heights).boxed().collect(Collectors.toList());
for (int i = 0; i < integers.size(); i++) {
if (i!=0){
if (integers.get(i)==integers.get(i-1)){
continue;
}
}
xl[i] = getBefore(integers,i,integers.get(i),integers.get(i))+getLast(integers,i,integers.get(i),0,integers.size());
}
Arrays.sort(xl);
return xl[heights.length-1];
}
public int getBefore(List<Integer> integers, int index, int current ,int beforeTotal){
if (index==0){
return beforeTotal;
}
if(integers.get(index - 1)<current){
return beforeTotal;
}else {
beforeTotal+=current;
index--;
beforeTotal = getBefore(integers,index,current,beforeTotal);
return beforeTotal;
}
}
public int getLast(List<Integer> integers, int index, int current ,int lastTotal,int length){
if (index==length-1){
return lastTotal;
}
if(integers.get(index + 1)<current){
return lastTotal;
}else {
lastTotal+=current;
index++;
lastTotal = getLast(integers,index,current,lastTotal,length);
return lastTotal;
}
}
}
2. 单调栈
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;
}
}
3. 判断极端情况的分治
class Solution {
public int largestRectangleArea(int[] heights) {
return largest(heights, 0, heights.length-1);
}
public int largest(int[] heights, int start, int end) {
if (start > end) {
return 0;
}
// 数组是递增的
boolean up = true;
boolean dowm = true;
// 先找最矮的
int minIndex = start;
for (int i = start + 1; i <= end; i++) {
if (heights[i] < heights[i - 1]) {
// 不是递增
up = false;
}
if (heights[i] > heights[i - 1]) {
// 不是递减
dowm = false;
}
if (heights[i] < heights[minIndex]) {
minIndex = i;
}
}
// 极端情况,递增或递减直接求面积
if (up) {
int maxArea = 0;
for (int i = start; i <= end; i++) {
maxArea = Math.max(maxArea, heights[i] * (end - i + 1));
}
return maxArea;
}
if (dowm) {
int maxArea = -1;
for (int i = end; i >= start; i--) {
maxArea = Math.max(maxArea, (i - start + 1) * heights[i]);
}
return maxArea;
}
// 当前层最大面积
int currArea = heights[minIndex] * (end - start + 1);
// 左边最大面积
int leftArea = largest(heights, start, minIndex - 1);
// 右边最大面积
int rightArea = largest(heights, minIndex + 1, end);
// 左右两边的最大面积
int currMaxArea = Math.max(leftArea, rightArea);
return Math.max(currArea, currMaxArea);
}
}