问题描述
小S最近在分析一个数组 h1,h2,...,hNh1,h2,...,hN,数组的每个元素代表某种高度。小S对这些高度感兴趣的是,当我们选取任意 kk 个相邻元素时,如何计算它们所能形成的最大矩形面积。
对于 kk 个相邻的元素,我们定义其矩形的最大面积为:
R(k)=k×min(h[i],h[i+1],...,h[i+k−1])R(k)=k×min(h[i],h[i+1],...,h[i+k−1])
即,R(k)R(k) 的值为这 kk 个相邻元素中的最小值乘以 kk。现在,小S希望你能帮他找出对于任意 kk,R(k)R(k) 的最大值。
测试样例
样例1:
输入:
n = 5, array = [1, 2, 3, 4, 5]
输出:9
样例2:
输入:
n = 6, array = [5, 4, 3, 2, 1, 6]
输出:9
样例3:
输入:
n = 4, array = [4, 4, 4, 4]
输出:16
代码如下
import java.util.*;
public class Main {
public static int solution(int n, int[] heights) {
if (n == 0) return 0;
// 添加哨兵元素
int[] newHeights = new int[n + 2];
System.arraycopy(heights, 0, newHeights, 1, n);
newHeights[0] = 0;
newHeights[n + 1] = 0;
int maxArea = 0;
Deque<Integer> stack = new ArrayDeque<>();
for (int i = 0; i < newHeights.length; i++) {
while (!stack.isEmpty() && newHeights[stack.peek()] > newHeights[i]) {
int height = newHeights[stack.pop()];
int width = i - stack.peek() - 1;
maxArea = Math.max(maxArea, height * width);
}
stack.push(i);
}
return maxArea;
}
public static void main(String[] args) {
System.out.println(solution(5, new int[]{1, 2, 3, 4, 5}) == 9);
System.out.println(solution(6, new int[]{5, 4, 3, 2, 1, 6}) == 9);
System.out.println(solution(4, new int[]{4, 4, 4, 4}) == 16);
}
}
代码逻辑解释
1. 哨兵元素的添加
int[] newHeights = new int[n + 2];
System.arraycopy(heights, 0, newHeights, 1, n);
newHeights[0] = 0;
newHeights[n + 1] = 0;
-
目的:为了避免处理数组边界问题,代码在原数组的两端各添加了一个高度为0的“哨兵”元素。
-
作用:
-
哨兵元素可以确保栈中的所有元素都能被处理,避免遗漏。
-
避免在遍历时因为数组越界而报错。
-
2. 单调栈的使用
Deque<Integer> stack = new ArrayDeque<>();
-
单调栈:栈中存储的是数组的索引,且栈内的元素对应的数组值是单调递增的。
-
作用:
-
通过单调栈可以快速找到每个柱子的左右边界,从而计算以该柱子为高度的最大矩形面积。
-
3. 遍历数组并处理栈
for (int i = 0; i < newHeights.length; i++) {
while (!stack.isEmpty() && newHeights[stack.peek()] > newHeights[i]) {
int height = newHeights[stack.pop()];
int width = i - stack.peek() - 1;
maxArea = Math.max(maxArea, height * width);
}
stack.push(i);
}
-
逻辑:
-
遍历数组:从左到右遍历数组
newHeights
。 -
栈的处理:
-
当前元素
newHeights[i]
小于栈顶元素对应的值时,说明栈顶元素的右边界已经找到。 -
弹出栈顶元素,计算以该元素为高度的矩形面积。
-
矩形的宽度为
i - stack.peek() - 1
,其中stack.peek()
是当前栈顶元素(即左边界)。
-
-
更新最大面积:每次计算矩形面积后,更新全局的最大面积。
-
入栈:将当前索引
i
压入栈中。
-
4. 返回结果
return maxArea;
-
最终返回计算得到的最大矩形面积。
代码的核心思想
-
单调栈:通过维护一个单调递增的栈,能够快速找到每个柱子的左右边界。
-
哨兵元素:避免边界问题,简化逻辑。
-
矩形面积计算:对于每个柱子,其矩形面积为
height * width
,其中height
是柱子的高度,width
是左右边界之间的距离。
时间复杂度
-
每个元素最多入栈和出栈一次,因此时间复杂度为 O(n)。
测试样例的解释
-
输入
[1, 2, 3, 4, 5]
:-
最大面积为 9,对应子数组
[3, 4, 5]
,高度为 3,宽度为 3。
-
-
输入
[5, 4, 3, 2, 1, 6]
:-
最大面积为 9,对应子数组
[4, 3, 2]
,高度为 3,宽度为 3。
-
-
输入
[4, 4, 4, 4]
:-
最大面积为 16,对应整个数组,高度为 4,宽度为 4。
-
总结
这段代码利用单调栈高效地解决了最大矩形面积问题,避免了暴力解法的高时间复杂度,同时通过哨兵元素简化了边界处理。