问题描述
(中等难度)小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) 的最大值。
思路解析
题目要求计算给定数组 h1, h2, ..., hN
中,对于任意 k
个相邻元素,所形成的最大矩形面积。矩形面积定义为 k
个相邻元素中的最小值乘以 k
。这个问题可以看作是一个扩展的“直方图中最大的矩形面积”问题,但不同之处在于我们需要考虑所有可能的 k
值。
直接枚举所有可能的 k
值并计算每个子数组的最小值会非常复杂且效率低下。为了优化这个问题,我们可以使用单调栈(monotonic stack)来解决。单调栈是一种数据结构,它能在 O(n) 的时间复杂度内找到数组中每个元素的左边和右边第一个比它小的元素的位置。
解题步骤
-
初始化:
创建一个空栈
stack
来存储数组元素的索引。 初始化max_area
为 0,用于记录最大的矩形面积。 初始化index
为 0,用于遍历数组。 -
遍历数组:
如果栈为空,或者当前元素
array[index]
大于等于栈顶元素对应的值array[stack[-1]]
,则将当前索引index
入栈。 否则,说明栈顶元素对应的值已经被当前元素“覆盖”,此时可以计算以栈顶元素为高度的矩形的最大面积。 -
计算面积:
当栈顶元素出栈时,计算以该元素为高度的矩形的宽度。宽度是栈顶元素索引与栈中下一个元素索引(或数组起始位置,如果栈为空)之间的距离。 面积等于高度乘以宽度。 更新
max_area
为当前面积和max_area
中的较大值。 -
处理剩余元素:
遍历结束后,栈中可能还有元素。这些元素对应的矩形宽度需要基于数组的末尾来计算。 重复计算面积和更新
max_area
的步骤。 -
返回结果:
返回
max_area
,即直方图中最大的矩形面积。
代码示例
def solution(n, array):
stack = []
max_area = 0
index = 0
# 遍历数组
while index < n:
# 如果栈为空,或当前元素大于等于栈顶元素对应的值,入栈
if not stack or array[index] >= array[stack[-1]]:
stack.append(index)
index += 1
else:
# 否则,栈顶元素出栈,并计算以该元素为高度的矩形的面积
top_of_stack = stack.pop()
width = (index - stack[-1] - 1) if stack else index
# 计算面积并更新最大面积
area = array[top_of_stack] * width
max_area = max(max_area, area)
# 处理栈中剩余的元素
while stack:
top_of_stack = stack.pop()
width = (index - stack[-1] - 1) if stack else index
area = array[top_of_stack] * width
max_area = max(max_area, area)
return max_area
个人思考
- 结合单调栈和枚举:一个可能的解决方案是结合单调栈和枚举。我们可以使用单调栈来快速找到每个元素左边和右边第一个比它小的元素的位置,这样我们就可以知道每个元素可以扩展成多宽的矩形(以该元素为最小值)。然后,我们可以枚举所有可能的宽度(即枚举所有可能的
k
值),并计算以每个元素为最小值的矩形的面积。 - 动态规划:另一种可能的解决方案是使用动态规划。我们可以定义一个二维数组
dp[i][j]
,表示以h[i]
到h[j]
为高度的矩形的最大面积。但是,这种方法的空间复杂度较高,且状态转移方程可能不太直观。 - 线段树或树状数组:对于更高效的解决方案,可以考虑使用线段树或树状数组等高级数据结构来快速查询任意区间的最小值。