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

98 阅读5分钟

问题简化

这道题目要求我们求得在给定的数组中,任意k个相邻元素所能形成的最大的矩形面积,其中,矩形面积是通过最小高度乘以这些元素个数来计算的,即R(k)=k×min(h[i],h[i+1],...,h[i+k1])R(k) = k × min ( h[i] , h[i+1] , ... , h[i+k−1] )

关键点:

  • 在数组中,选取任意连续的k个元素。
  • 对于每个选定的k个相邻元素,计算其矩形面积,即这k个元素的最小值乘以k
  • 找出所有可能的k个元素中的最大矩形面积。

解题思路

对于这道题我们很容易想到使用暴力枚举来解题,即——对每个可能的k,枚举所有可能的连续子数组,计算每个子数组的最小值,然后计算面积。这种思路非常简单且直观,但这种办法的时间复杂度为O(n2)O(n2),如果问题的规模提高,这种办法的可行度很低。

因此,我们需要一个新的解法。根据问题关键点,我们可以知道我们要对连续子数组的最小值进行计算,并且每个子数组的计算可能会重复很多次,由此,我们可以尝试使用来加速这个过程。

使用栈来优化寻找每个元素的最大矩形面积:

  • 单调栈:通过单调栈来维护每个元素的前后限制,从而避免重复计算每个元素的最小值。

    • 左边界:使用栈来记录每个元素左侧第一个比它小的位置
    • 右边界:使用栈来记录每个元素右侧第一个比它小的位置

通过单调栈,就可以在 O(n)O(n) 时间内解决掉这个最大矩形面积问题。

代码实现

以下是python的具体实现

1. 初始化

stack = []
max_area = 0

left = [0] * n
right = [0] * n
  • stack: 用来辅助计算每个元素的左边界和右边界。
  • max_area: 记录最大矩形面积,初始值为 0。
  • left: 一个长度为 n 的数组,用来存储每个元素的左边界,即比该元素小的第一个位置。
  • right: 一个长度为 n 的数组,用来存储每个元素的右边界,即比该元素小的第一个位置。

2. 计算每个元素的左边界

for i in range(n):
    while stack and array[stack[-1]] >= array[i]:
        stack.pop()
    left[i] = stack[-1] if stack else -1
    stack.append(i)
  • 遍历数组:从左到右遍历数组,找到每个元素的左边界。

  • 栈的作用

    • while stack and array[stack[-1]] >= array[i]: 这个循环的作用是,当栈中的元素比当前元素大或相等时,出栈。因为我们需要找到第一个比当前元素小的元素。
    • left[i] = stack[-1] if stack else -1: 如果栈不为空,则栈顶的元素就是当前元素的左边界;如果栈为空,说明当前元素的左边没有比它小的元素,那么左边界为 -1。
    • stack.append(i): 把当前元素的索引入栈,待后续元素的左边界需要参考它。

3. 计算每个元素的右边界

stack.clear()

for i in range(n - 1, -1, -1):
    while stack and array[stack[-1]] > array[i]:
        stack.pop()
    right[i] = stack[-1] if stack else n
    stack.append(i)
  • 遍历数组:这次从右到左遍历数组,计算每个元素的右边界。

  • 栈的作用

    • while stack and array[stack[-1]] > array[i]: 这个循环的作用是,当栈中的元素大于当前元素时,出栈。因为我们需要找到第一个比当前元素小的元素。
    • right[i] = stack[-1] if stack else n: 如果栈不为空,则栈顶的元素就是当前元素的右边界;如果栈为空,说明当前元素的右边没有比它小的元素,那么右边界为 n(即数组的长度,代表到数组的末尾)。
    • stack.append(i): 把当前元素的索引入栈,待后续元素的右边界需要参考它。

4. 计算最大矩形面积

for i in range(n):
    k = right[i] - left[i] - 1
    current_area = k * array[i]
    max_area = max(max_area, current_area)
  • 计算宽度:对于每个元素 i,它的宽度 k 是由右边界 right[i] 和左边界 left[i] 之间的距离来决定的。宽度公式是 right[i] - left[i] - 1

    • right[i] 是第一个比 array[i] 小的元素的索引。
    • left[i] 是第一个比 array[i] 小的元素的索引。
    • 宽度是右边界减去左边界再减去 1,因为左右边界的索引本身不包括在矩形内。
  • 计算面积:面积 current_area 是宽度 k 乘以高度 array[i]

  • 更新最大面积:更新 max_area 为当前面积与已有的最大面积之间的较大值。

5. 返回最大矩形面积

return max_area
  • 最后,返回 max_area,即最大矩形面积。

总结

这道题不仅锻炼了我对栈的运用(例如,在遇到处理“区间”和“边界”问题时,栈就非常好用了),还教我如何从数据结构的角度来优化解题策略。利用 单调栈 来维护每个元素的“左边界”和“右边界”,转变了我从“穷举所有可能”到“利用数据结构精确高效查找边界”的思路。由此可见,我们要学会分析问题的性质,只有这样,才能更好地解决未来可能遇到的大规模数据和更复杂的算法问题。

补充

什么是单调栈

单调栈(Monotone Stack) :这是一种特殊的栈,它在栈的后进先出规则上,还要求从 栈顶 到 栈底 的元素是单调递增(或者单调递减) 的栈,就是单调栈

单调递增栈: 满足从栈顶到栈底的元素是单调递增的栈(只有比栈顶元素小的元素才能直接入栈,否则需要先将栈中比当前元素小的元素出栈,再将当前元素入栈)

单调递减栈: 满足从栈顶到栈底的元素是单调递减的栈(只有比栈顶元素大的元素才能直接入栈,否则需要先将栈中比当前元素大的元素出栈,再将当前元素入栈)