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

117 阅读6分钟

问题描述

小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


问题背景

小S正在分析一个数组,数组中的每个元素代表某种高度。他感兴趣的是,当我们选取任意 k 个相邻元素时,如何计算它们所能形成的最大矩形面积。具体来说,对于 k 个相邻的元素,我们定义其矩形的最大面积为:

R(k)=k×min(h[i],h[i+1],...,h[i+k−1])

即,R(k) 的值为这 k 个相邻元素中的最小值乘以 k。小S希望你能帮他找出对于任意 k,R(k) 的最大值。

思路分析

  1. 问题理解

    • 我们需要找到数组中任意 k 个相邻元素所能形成的最大矩形面积。
    • 这个矩形的面积由 k 个元素中的最小值决定,乘以 k。
  2. 数据结构选择

    • 为了高效地找到每个元素作为最小值时所能形成的最大矩形面积,我们可以使用单调栈。
    • 单调栈可以帮助我们快速找到每个元素作为最小值时的左右边界。
  3. 算法步骤

    • 使用两个数组 left 和 right 分别记录每个元素作为最小值时的左边界和右边界。
    • 通过单调栈计算 left 和 right 数组。
    • 遍历数组,计算每个元素作为最小值时所能形成的矩形面积,并更新最大面积。

直接方法

一种直接但低效的方法是对每一个可能的 K 进行两层循环,寻找每一段 K 个元素中的最小值并计算面积,最后比较得到最大值。这种方法的时间复杂度为 O(n^2),当数组长度较大时会非常耗时。

单调栈优化

为了提高效率,可以采用单调栈的方法来解决这个问题。通过维护一个递增的栈,我们可以快速找到每个高度作为矩形的高时,所能向左和向右扩展的最大范围。这样就能在 O(n) 的时间内计算出所有可能的最大矩形面积。

具体步骤如下:

  1. 使用单调栈计算每个位置的左侧边界,即该位置向左延伸直到遇到比它矮的第一个柱子的位置。
  2. 类似地,计算每个位置的右侧边界。
  3. 对于每个位置,用高度乘以左右边界的距离来计算可能的最大矩形面积。
  4. 在所有计算出的面积中选择最大的一个。

代码分析

int solution(int n, std::vector<int>& A) {

定义了一个名为 solution 的函数,接受一个整数 n 和一个整数向量 A 作为参数,返回一个整数。

int max_area = 0;

初始化一个变量 max_area 为 0,用于存储最终的最大矩形面积。

std::vector<int> left(n), right(n);

定义两个向量 left 和 right,大小均为 n,分别用于存储每个位置的左侧边界和右侧边界。

std::vector<int> stack;

定义一个栈 stack,用于辅助计算边界。

// 初始化栈和计算左侧边界
    for (int i = 0; i < n; ++i) {
        while (!stack.empty() && A[stack.back()] >= A[i]) {
            stack.pop_back();
        }
        left[i] = stack.empty() ? 0 : stack.back() + 1;
        stack.push_back(i);
    }

这段代码用于计算每个位置的左侧边界: 外层循环遍历数组 A 的每个元素。 内层循环检查栈顶元素的高度是否大于或等于当前元素的高度,如果是,则弹出栈顶元素。 如果栈为空,说明当前元素左边没有更矮的柱子,因此 left[i] 设为 0;否则,left[i] 设为栈顶元素的下一个位置。 将当前索引 i 压入栈中。

// 清空栈并计算右侧边界
    stack.clear();
    for (int i = n - 1; i >= 0; --i) {
        while (!stack.empty() && A[stack.back()] >= A[i]) {
            stack.pop_back();
        }
        right[i] = stack.empty() ? n : stack.back();
        stack.push_back(i);
    }

这段代码用于计算每个位置的右侧边界: 首先清空栈。 外层循环从数组 A 的最后一个元素开始向前遍历。 内层循环检查栈顶元素的高度是否大于或等于当前元素的高度,如果是,则弹出栈顶元素。 如果栈为空,说明当前元素右边没有更矮的柱子,因此 right[i] 设为 n;否则,right[i] 设为栈顶元素的位置。 将当前索引 i 压入栈中。

// 计算最大矩形面积
    for (int i = 0; i < n; ++i) {
        max_area = std::max(max_area, A[i] * (right[i] - left[i]));
    }

这段代码用于计算每个位置的最大矩形面积: 遍历数组 A 的每个元素。 计算以当前高度 A[i] 为高的矩形面积,宽度为 right[i] - left[i]。 更新 max_area 为当前计算的面积和已知最大面积中的较大者。

return max_area;

返回计算得到的最大矩形面积。

心得分析

  1. 单调栈的应用

    • 单调栈是一种非常强大的数据结构,特别适用于解决需要找到某个元素的左右边界的问题。
    • 通过单调栈,我们可以在 O(n) 的时间复杂度内计算出每个元素的左右边界,从而高效地解决问题。
  2. 代码优化

    • 在实际应用中,我们可以进一步优化代码,例如减少不必要的数组访问和计算。
    • 通过合理的数据结构选择和算法设计,我们可以显著提高代码的性能和可读性。
  3. 问题抽象

    • 通过抽象问题,我们可以将复杂的问题简化为一系列简单的步骤,从而更容易找到解决方案。
    • 在解决类似问题时,我们可以借鉴已有的算法和数据结构,从而更快地找到解决方案。

通过这道题目,我们不仅学会了如何使用单调栈解决最大矩形面积问题,还加深了对数据结构和算法设计的理解。