问题描述
给定一个数组 h[1], h[2], ..., h[N],每个元素代表某种高度。要求计算当选取任意 k 个相邻元素时,它们所能形成的最大矩形面积。这里的矩形面积定义为 k 个相邻元素中的最小值乘以 k。不过,题目实际上可以通过一种转化,理解为求每个元素作为矩形高度时能够扩展的最大宽度,然后计算面积。
思路
- 单调栈:使用单调栈来找到每个元素左边和右边第一个比它小的元素的位置。
- 计算面积:对于每个元素,以其高度作为矩形的高度,计算以其为最小高度的矩形的最大宽度(即左右边界之间的距离),然后计算面积。
- 最大面积:在所有可能的矩形面积中找出最大值。
图解
假设数组为 [5, 4, 3, 2, 1, 6]:
-
单调栈计算左右边界:
- 使用单调递减栈,从左到右遍历数组,记录每个元素左边第一个比它小的元素的位置(存储在
left数组中)。 - 从右到左遍历数组,记录每个元素右边第一个比它小的元素的位置(存储在
right数组中)。
- 使用单调递减栈,从左到右遍历数组,记录每个元素左边第一个比它小的元素的位置(存储在
-
计算面积:
- 对于每个元素
array[i],其宽度为right[i] - left[i] - 1。 - 面积则为
array[i] * width。
- 对于每个元素
-
找出最大面积:
- 遍历所有元素,计算其面积,并找出最大值。
代码详解
import java.util.Stack;
public class Main {
public static int solution(int n, int[] array) {
int[] left = new int[n];
int[] right = new int[n];
Stack<Integer> stack = new Stack<>();
for (int i = 0; i < n; i++) {
while (!stack.isEmpty() && array[stack.peek()] >= array[i]) {
stack.pop();
}
left[i] = stack.isEmpty() ? -1 : stack.peek();
stack.push(i);
}
stack.clear();
for (int i = n - 1; i >= 0; i--) {
while (!stack.isEmpty() && array[stack.peek()] >= array[i]) {
stack.pop();
}
right[i] = stack.isEmpty() ? n : stack.peek();
stack.push(i);
}
int maxArea = 0;
for (int i = 0; i < n; i++) {
int width = right[i] - left[i] - 1;
int area = array[i] * width;
maxArea = Math.max(maxArea, area);
}
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);
}
}
知识总结
- 单调栈:单调栈是一种特殊的栈结构,用来解决一些与“单调性”有关的问题,比如求数组中每个元素的左边/右边第一个比它大/小的元素的位置。
- 时间复杂度:使用单调栈可以在
O(n)时间复杂度内完成上述任务,非常高效。
理解
单调栈的核心思想是利用栈的单调性来快速找到满足条件的元素位置。在这个问题中,通过维护一个单调递减的栈,我们可以方便地找到每个元素左边和右边第一个比它小的元素,从而计算出以该元素为最小高度的矩形的最大宽度。
学习建议
- 理解单调栈的性质:单调栈之所以高效,是因为它利用了栈的后进先出特性,以及维护了一个单调的序列。
- 多做练习:通过多做相关题目,熟悉单调栈在不同场景下的应用。