问题描述
小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
暴力解法
暴力解法的思路是对于每一个可能的窗口大小 k(从1到数组的长度 len),我们遍历数组,计算每个窗口中的最小高度,并计算矩形面积。以下是详细步骤:
- 对于每个可能的窗口大小
k,我们遍历数组,确定每个窗口的起始位置i。 - 对于每个窗口,我们找出窗口内最小的元素
tempMin。 - 计算当前窗口的面积
k * tempMin,并与当前记录的最大面积max进行比较,如果更大,则更新max。 - 当遍历完所有窗口后,
max就是我们要找的最大矩形面积。
以下是暴力解法的代码实现:
public class Main {
public static int solution(int n, int[] array) {
// Edit your code here
int max = 0;
int len = array.length;
for (int k = 1; k <= len; k++) {
for (int i = 0; i <= len - k; i++) {
int tempMin = array[i];
for (int j = i + 1; j < i + k; j++) {
if (tempMin > array[j]) {
tempMin = array[j];
}
}
if (max < k * tempMin) {
max = k * tempMin;
}
}
}
return max;
}
public static void main(String[] args) {
// Add your test cases here
System.out.println(solution(5, new int[] { 1, 2, 3, 4, 5 }) == 9);
}
}
滑动窗口法
滑动窗口法优化了暴力解法中的重复计算问题。我们使用一个双端队列(deque)来维护一个单调递增的序列,该序列的元素对应数组中的索引。以下是详细步骤:
- 对于每个可能的窗口大小
k,我们初始化一个双端队列,并处理第一个窗口。 - 在第一个窗口中,我们确保队列中的元素对应的数组值是递增的。如果新元素小于队列末尾的元素,我们将队列末尾的元素弹出,直到找到合适的位置。
- 队列的头部元素始终是当前窗口的最小值,因此我们可以直接计算面积
k * array[deque.peekFirst()]并更新最大面积max。 - 然后我们滑动窗口,每次移动一位。在滑动时,我们移除不再窗口内的元素,并继续维护队列的单调性。
- 每次窗口移动后,我们更新最大面积
max。
以下是滑动窗口法的代码实现:
import java.util.ArrayDeque;
import java.util.Deque;
public class Main {
public static int solution(int n, int[] array) {
int max = 0;
int len = array.length;
// 遍历每个可能的窗口大小 k
for (int k = 1; k <= len; k++) {
Deque<Integer> deque = new ArrayDeque<>();
// 初始化第一个窗口的值
for (int i = 0; i < k; i++) {
while (!deque.isEmpty() && array[deque.peekLast()] >= array[i]) {
deque.pollLast();
}
deque.offerLast(i);
}
max = Math.max(max, k * array[deque.peekFirst()]);
// 滑动窗口
for (int i = k; i < len; i++) {
// 移除不在窗口内的元素
if (!deque.isEmpty() && deque.peekFirst() <= i - k) {
deque.pollFirst();
}
// 维护单调递增队列
while (!deque.isEmpty() && array[deque.peekLast()] >= array[i]) {
deque.pollLast();
}
deque.offerLast(i);
// 更新最大值
max = Math.max(max, k * array[deque.peekFirst()]);
}
}
return max;
}
public static void main(String[] args) {
// Add your test cases here
System.out.println(solution(5, new int[] { 1, 2, 3, 4, 5 }) == 9);
}
}