最大矩形面积问题 | 豆包MarsCode AI刷题

44 阅读3分钟

问题描述

小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),我们遍历数组,计算每个窗口中的最小高度,并计算矩形面积。以下是详细步骤:

  1. 对于每个可能的窗口大小 k,我们遍历数组,确定每个窗口的起始位置 i
  2. 对于每个窗口,我们找出窗口内最小的元素 tempMin
  3. 计算当前窗口的面积 k * tempMin,并与当前记录的最大面积 max 进行比较,如果更大,则更新 max
  4. 当遍历完所有窗口后,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)来维护一个单调递增的序列,该序列的元素对应数组中的索引。以下是详细步骤:

  1. 对于每个可能的窗口大小 k,我们初始化一个双端队列,并处理第一个窗口。
  2. 在第一个窗口中,我们确保队列中的元素对应的数组值是递增的。如果新元素小于队列末尾的元素,我们将队列末尾的元素弹出,直到找到合适的位置。
  3. 队列的头部元素始终是当前窗口的最小值,因此我们可以直接计算面积 k * array[deque.peekFirst()] 并更新最大面积 max
  4. 然后我们滑动窗口,每次移动一位。在滑动时,我们移除不再窗口内的元素,并继续维护队列的单调性。
  5. 每次窗口移动后,我们更新最大面积 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);
    }
}