题目
给定非负整数数组 heights ,数组中的数字用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1 。
求在该柱状图中,能够勾勒出来的矩形的最大面积。
示例 1:
输入:heights = [2,1,5,6,2,3]
输出:10
解释:最大的矩形为图中红色区域,面积为 10
示例2:
输入: heights = [2,4]
输出: 4
提示:
1 <= heights.length <=105
0 <= heights[i] <= 104
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/0ynMMM
一、解题思路
思路:
暴力枚举的一种思路是枚举某一根柱子,将其高度固定为矩形的高度h,随后两边延伸,寻找高度比h小的柱子,也就是矩形的边界,当左右两边界宽度为w,那么对应的矩形面积为 w * h。
枚举过程中选取最大的面积,答案也就出来了。其时间复杂度为O(n2)会超出时间限制。
所以根据此思路,做点优化。
单调栈:
找左边界:
在讲单调栈前,我们需要明确知道在向左延伸时,什么情况下,哪根柱子会有可能成为左边界:
有3个下标: i1 < i2 < i3,找 i3的左边界
假设heights[ i1] >= heights[ i2] :
①当 heights[ i3] > heights[ i2] ,那么 i3 的左边界就是 i2;
②当 heights[ i3] <= heights[ i2] ,因为要找高度比 i3小的,所以 i1,i2都不是
假设heights[ i1] < heights[ i2] :
① 当 heights[ i3] > heights[ i2] ,那么 i3的左边界就是 i2
② 当 heights[ i3] = heights[ i2],那么 i3的左边界就是 i1
③ 当 heights[ i3] < heights[ i2],heights[ i3] > heights[ i1],那么i3的左边界就是i1
④ 当 heights[ i3] < heights[ i2],heights[ i3] < heights[ i1],i1,i2都不是
从上面的结论就可以知道当 heights[ i1] >= heights[ i2] 且 i1<i2,那么无论 i3的高度如何,i1怎么也不可能成为左边界。只有 i1<i2这种递增的柱子才有可能成为边界
所以我们可以根据栈先进后出的特性,遍历存储有可能成为边界的柱子的下标,形成一个自底向上递增的单调栈:
因为每根柱子都有进栈机会,所以不设入栈条件
出栈条件:
栈不为空且 栈顶对应的柱子 高度大于 当前柱子 ,栈顶元素出栈,重复此过程,直到栈顶对应的高度小于 当前柱子(此栈顶对应的柱子就是左边界)或栈空。我们可以假设一个虚拟柱子的下标为-1,其高度无限低,栈空时,也就是虚拟柱子当左边界。
照相同思路,从右往左遍历,也可确定右边界。确定了左右边界也就能知道最后的面积了。
优化:
我们也可以从出栈时明确右边界,
当我们要找当前栈顶对应的柱子的右边界,我们要怎么找呢?
当 当前栈顶元素被弹出时,也就是遇到一个柱子其高度比栈顶对应柱子低或相同。。。
也就是当栈顶元素被弹出时,必定有一个高度比它低或相同的柱子进来了,那么这根柱子就是栈顶柱子的右边界
有人会感觉有点不对劲了,如果两者高度相同的话,那么对栈顶对应的柱子来说,右边界是不是不正确。
答案是,确实算的不正确,但是对最终的结果没有影响的,因为高度相同嘛,虽说对于栈顶对应的柱子来说右边界是少了,但是对于当前要进来的这根柱子,是可以算出正确的右边界的。(高度相同,所以当前进来的这根柱子会把正确右边界求出)
在遍历结束后,如果栈中还有元素,那么其右边界位置即为最右边,也就是heights数字的长度n.
二、代码
class Solution {
public int largestRectangleArea(int[] heights) {
int n = heights.length;
int[] left = new int[n];
int[] right = new int[n];
//为右边界定值n,如果最后剩下的数没机会pop,那么右边界就是n
//例如示例1 中的1,2,3都没机会pop,那么其右边界就是n
Arrays.fill(right,n);
Deque<Integer> eq = new LinkedList<>();
for(int i=0;i<n;i++){
//heights数组中每个数都有机会入栈,所以代码思路不写条件入栈,因为都会入
//写出栈条件
//持续出栈,直到heights[eq.peek()]<heights[i]
while(!eq.isEmpty()&&heights[eq.peek()]>=heights[i]){
right[eq.pop()]=i;
}
//先不入栈,查看栈顶,把左边界放进去
left[i]= eq.isEmpty() ? -1:eq.peek();
eq.push(i);
}
int res = 0;
for(int i=0;i<n;i++){
//宽乘高
res=Math.max(res,(right[i]-left[i]-1)*heights[i]);
}
return res;
}
}
参考力扣解析:
https://leetcode-cn.com/problems/0ynMMM/solution/zhi-fang-tu-zui-da-ju-xing-mian-ji-by-le-pcyu/
侵删