题目描述
小S最近在分析一个数组 ,数组的每个元素代表某种高度。小S对这些高度感兴趣的是,当我们选取任意个相邻元素时,如何计算它们所能形成的最大矩形面积。
对于个相邻的元素,我们定义其矩形的最大面积为:
即, 的值为这 个相邻元素中的最小值乘以 。
现在,小S希望你能帮他找出对于任意 , 的最大值。
解题思路
这个题目其实就是求直方图中能形成的最大矩形面积,我们可以将题目重新描述成如下:
-
问题描述: 给定一个由
n个非负整数表示的直方图,其中每个整数代表直方图中柱形的高度,且每个柱形宽度为 1,找出能够形成的最大矩形的面积。 -
核心思想:
- 每个柱形都作为该矩形的可能最小高度,向左和向右扩展,直到遇到比它矮的柱子。那么对于这个柱形,它可以向左扩展到left,向右扩展到right。那么它的矩形面积的宽就是right-left-1。高就是当前这个柱形的高。
- 我们可以运用单调栈来高效地计算每个柱形向左和向右能扩展到的边界。
关键步骤
-
初始化:
- 创建两个数组
left和right。 left[i]表示第i个柱形左侧第一个小于array[i]的柱子的索引。right[i]表示第i个柱形右侧第一个小于array[i]的柱子的索引。stack用于作为单调栈来存储每根柱子的索引。
- 创建两个数组
-
计算
left数组:- 从左到右遍历
array。 - 使用单调栈维护一个从栈底到栈顶递增的索引序列。
- 对于每个柱形
array[i],当栈顶的柱形高度大于等于array[i]时,弹出栈顶元素。因为这个栈顶元素一定不是下标i右边元素的left了。 - 如果栈不为空,则
left[i] = stack.getLast(),否则left[i] = -1(表示左侧没有更小的柱子)。 - 将当前索引
i压入栈。
- 从左到右遍历
-
计算
right数组:- 从右到左遍历
array。 - 使用单调栈维护一个从栈底到栈顶递增的索引序列。
- 对于每个柱形
array[i],当栈顶的柱形高度大于等于array[i]时,弹出栈顶元素。因为这个栈顶元素一定不是下标i左边元素的right了。 - 如果栈不为空,则
right[i] = stack.getLast(),否则right[i] = n(表示右侧没有更小的柱子)。 - 将当前索引
i压入栈。
- 从右到左遍历
-
计算最大矩形面积:
- 遍历每个柱形,计算以
array[i]为高度的最大矩形的宽度:right[i] - left[i] - 1。 - 更新最大矩形面积
ans。
- 遍历每个柱形,计算以
-
返回结果:
- 输出并返回最大矩形面积
ans。
- 输出并返回最大矩形面积
代码
public static int solution(int n, int[] array) {
LinkedList<Integer> stack =new LinkedList<>();
int[] left =new int[n];
for(int i=0;i<n;++i){
left[i]=-1;
}
//left[i]为下标i的元素最左边第一个小于array[i]的下标
for(int i=0;i<n;++i){
while(!stack.isEmpty()&&array[i]<=array[stack.getLast()]){
//此时栈顶元素没用了
stack.removeLast();
}
if(!stack.isEmpty()){
left[i]=stack.getLast();
}
stack.addLast(i);
}
stack.clear();
int[] right =new int[n];
for(int i=0;i<n;++i){
right[i]=n;
}
//right[i]为下标i的元素最右边第一个小于array[i]的下标
for(int i=n-1;i>=0;--i){
while(!stack.isEmpty()&&array[i]<=array[stack.getLast()]){
//此时栈顶元素没用了
stack.removeLast();
}
if(!stack.isEmpty()){
right[i]=stack.getLast();
}
stack.addLast(i);
}
int ans=0;
for(int i=0;i<n;i++){
ans = Math.max(ans,array[i]*(right[i]-left[i]-1));
}
return ans;
}
算法复杂度
时间复杂度:O(n),3个for循环。 空间复杂度:O(n),因为left数组,right数组以及栈大小为n。