84.柱状图中最大的矩形
题目要点
关于求解这道题有两点
(1)是多个柱子组成的矩形的高度,一定是由最低的一根柱子决定的
以当前柱子为最低高度,遍历整个数组,求出所有的面积可能
矩形面积=最低柱子高度*柱子数(大于等于最低高度且互相相邻)
(2)如何确定对应矩形的柱子数
以当前柱子i为最低高度,则需要找到对应大于等于当前柱子i的柱子数,分别向左和向右,找到柱子高度小于当前柱子高度的坐标left和right,right-left+1就是对应柱子数
解法
方法1-直接暴力求解
public int largestRectangleArea(int[] heights) {
int max=0;
for(int i=0;i<heights.length;i++){
int left=i;
int right=i;
while(left-1>=0&&heights[left-1]>=heights[i]) left++;//往左找到坐标
//往右找到坐标
while(right+1<heights.length&&heights[right+1]>=heights[i]) right++;
max=Math.max(max,heights[i]*(right-left+1));
}
return max;
}
显然两层for循环,时间复杂度 O(n^2)
方法2-优化暴力法
优化方式-记录之前的left和right
以left[]为例
left[i-1], 如果heights[i]>=heights[i-1],那么heights[i]一定大于等于left[i-1]和i之间的柱子,直接和left[i-1]比较就行
public int largestRectangleArea2(int[] heights) {
int max=0;
int left[]=new int [heights.length];
int right[]=new int [heights.length];
for(int i=0;i<heights.length;i++){//往左找到坐标
int index=i-1;
while(index>=0&&heights[index]>=heights[i]) index=left[index];
left[i]=index;
}
//注意这里要从后往前找
for(int i=heights.length-1;i>=0;i--){//往右找到坐标
int index=i+1;
while(index<heights.length&&heights[index]>=heights[i]) index=right[index];
right[i]=index;
max = Math.max(max, (right[i] - left[i] - 1) * heights[i]);
}
return max;
}
方法3-单调递增栈
通过单调递增栈,可以只使用O(n)的代价得到对于一个柱面高度,不小于这个高度的柱面数量
向右遍历时面积求法
area= (i−stack[top−1]−1)×heights[stack[top]]
弹出一个数,就会计算一次面积
需要入栈的数i,heights[i]<=heights[stack[top] 这是弹出栈顶元素的条件
stack[top−1] 栈顶元素的前一个数 stack[top−1]< stack[top]
就这样刚好找到了 栈顶元素的left和right
遍历完成后,弹出所有数据
area= heights[stack.pop()]*(heights.length-stack[top−1]-1)
heights [2,4,6,5,3]
从前往后走
加入2 stack [-1,0]
加入4 stack [-1,0,1]
加入6 stack [-1,0,1,2]
加入5(5<6) 弹出6 大于等于6的柱面长度为 heights[2]=6 area=6*(3-1-1) =6
stack [-1,0,1,3]
加入3 弹出5 heights[1]=4是小于5的第一个左边数 heights[4]=3是小于5 第一个右边的数
area= (第一个右边的数-第一个左边数-1)*5 =2*5
stack [-1,0,1,4]
弹出4 area=4*(4-0-1)
stack [-1,0,4]
此时走到了尾部 需要弹出剩下的数据
由于是单调递增栈
对于此时栈中存在元素,它一定小于后面所有元素
弹出3
area= (5 -0-1)*3 =3*4
stack [-1,0]
弹出0
area= (5-(-)-1)*2 =5*2
stack [-1]
此时得到结果 12
根据上述例子,可知通过单调栈
public int largestRectangleArea3(int[] heights) {//单调栈
int max=0;
Stack<Integer> stack=new Stack();
stack.push(-1);
for(int i=0;i<heights.length;i++){//往左找到坐标
while(stack.peek()!=-1&&heights[i]<=heights[stack.peek()]) {
max=Math.max(max,heights[stack.pop()]*(i-stack.peek()-1));
}
stack.push(i);
}
while(stack.peek()!=-1) {
max=Math.max(max,heights[stack.pop()]*(heights.length-stack.peek()-1));
}
return max;
}
遍历数组一遍,时间复杂度O(n)
参考:leetcode上的解法
https://leetcode-cn.com/problems/largest-rectangle-in-histogram/solution/zhao-liang-bian-di-yi-ge-xiao-yu-ta-de-zhi-by-powc/