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

56 阅读5分钟

题目解析

题目要求我们计算数组中所有可能的 连续子数组长度为 k 的最大矩形面积,公式为 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) 中的最大值。


解题思路

暴力解法

  1. 思路:对于每一个可能的 kk(子数组长度),遍历所有可能的起点 ii,计算长度为 kk 的子数组的最小值,进而计算面积 k×mink×min。

    • 遍历 kk 的范围:从 1 到 nn。
    • 对于每个 kk,找出所有可能的子数组,计算其最小值和面积。
    • 更新全局最大面积。
  2. 时间复杂度

    • 外层循环 kk 为 O(n)O(n)。
    • 内层计算所有子数组起点 ii 为 O(n)O(n)。
    • 每个子数组的最小值计算 O(k)O(k)。
    • 总复杂度约为 O(n3)O(n3),适用于小规模数据。

优化解法

通过优化计算最小值的过程,降低时间复杂度:

  1. 滑动窗口法

    • 使用双端队列维护当前窗口的最小值索引,从而快速获取最小值。
    • 滑动窗口的移动和最小值维护的复杂度为 O(n)O(n)。
    • 时间复杂度降低至 O(n2)O(n2)。
  2. 单调栈法

    • 利用单调栈计算柱状图的最大矩形面积,等价于题目问题。
    • 此方法的时间复杂度进一步优化为 O(n)O(n)。

图解

以数组 [5, 4, 3, 2, 1, 6] 为例:

  1. 当 k=1k=1:所有单个高度的矩形面积是 [5, 4, 3, 2, 1, 6],最大值为 6。

  2. 当 k=2k=2:子数组有 [5,4], [4,3], [3,2], [2,1], [1,6],面积分别为 [8, 6, 4, 2, 2],最大值为 8。

  3. 当 k=3k=3:子数组有 [5,4,3], [4,3,2], [3,2,1], [2,1,6],面积分别为 [9, 6, 3, 6],最大值为 9。

    • 重复上述步骤找到所有可能面积的最大值。

代码实现

暴力解法

def solution(n, array):
    max_area = 0
    # 遍历所有可能的 k 值
    for k in range(1, n + 1):
        for i in range(n - k + 1):
            # 计算 k 个相邻元素的最小值
            min_height = min(array[i:i + k])
            # 计算矩形面积
            area = k * min_height
            # 更新最大面积
            max_area = max(max_area, area)
    return max_area

优化解法(滑动窗口)

from collections import deque

def solution(n, array):
    max_area = 0
    for k in range(1, n + 1):
        min_deque = deque()  # 双端队列
        for i in range(n):
            # 移出窗口左侧
            if min_deque and min_deque[0] < i - k + 1:
                min_deque.popleft()
            # 维护单调递增队列
            while min_deque and array[min_deque[-1]] >= array[i]:
                min_deque.pop()
            min_deque.append(i)
            # 如果窗口满了,计算面积
            if i >= k - 1:
                max_area = max(max_area, k * array[min_deque[0]])
    return max_area

在解决这个问题的过程中,涉及以下知识点:


1. 数组操作

  • 切片:通过 array[i:i+k] 获取数组的子数组。
  • 遍历:遍历数组的所有元素或所有子数组的起始位置。

2. 暴力枚举

  • 逐步增加范围:从最小的子数组(长度为 1)到完整数组(长度为 n),依次计算所有可能的矩形面积。
  • 时间复杂度分析:通过逐层嵌套循环,分析程序效率。

3. 优化算法

  • 滑动窗口

    • 利用双端队列维护当前窗口的最小值。
    • 通过窗口的移动来避免重复计算,提高效率。
    • 双端队列的操作(插入、删除、更新)时间复杂度为 O(1)O(1)。
  • 单调栈

    • 单调栈是一种特殊的数据结构,可以快速处理连续数据的特定属性(如柱状图中的最大矩形问题)。
    • 栈中保持元素单调递增或递减,以快速查找高度的左右边界。
    • 在当前问题中,用单调栈解决最大矩形问题,把每个元素视为柱状图中的柱子。

4. 数学公式

  • 面积公式:矩形面积为 宽度 × 高度,其中高度是子数组的最小值,宽度是子数组长度 kk。

  • 动态维护最小值

    • 在暴力解法中,直接调用 Python 内置函数 min()
    • 在滑动窗口中,动态维护窗口内的最小值索引。
    • 在单调栈中,利用高度递增(或递减)性质找到最小值的左右边界。

5. 时间复杂度分析

  • 暴力解法

    • 三重循环,复杂度为 O(n3)O(n3)。
  • 滑动窗口

    • 双层循环,复杂度降低至 O(n2)O(n2)。
  • 单调栈

    • 每个元素最多被入栈和出栈一次,复杂度为 O(n)O(n)。

6. 代码结构

  • 函数封装

    • 定义函数 solution(n, array),传入长度 nn 和数组 array
  • 模块化测试

    • 使用 if __name__ == "__main__": 测试函数,验证代码正确性。
  • 可维护性

    • 将暴力解法、滑动窗口法、单调栈法分成独立模块,便于理解和扩展。

7. 边界处理

  • 特殊情况

    • 数组长度为 1:直接返回单个元素。
    • 数组元素相等:返回 n×h[0]n×h[0]。
  • 哨兵技巧

    • 在单调栈方法中,添加一个高度为 0 的哨兵,简化边界处理逻辑。

8. 常用数据结构

  • 双端队列(deque)

    • 从 collections 模块中导入,用于维护滑动窗口中的最小值。
    • 支持快速的插入和删除操作。
    • 用于单调栈法中存储元素索引,便于计算高度和宽度的关系。

9. Python内置函数

  • min():计算数组中的最小值(暴力解法中使用)。
  • max():更新全局最大矩形面积。
  • append() 和 pop():操作双端队列和栈。

总结

  1. 暴力解法适合小规模数据,逻辑简单但效率低。
  2. 滑动窗口优化部分重复计算,时间复杂度降低至 O(n2)O(n2)。
  3. 单调栈法结合柱状图思路,直接计算最大矩形面积,效率最高 O(n)O(n)。