题目分析与解法思路
一、题目分析
给定一个数组 h,要求我们对于任意长度为 k (1 ≤ k ≤ N)的一组相邻元素,计算该组元素所能形成的最大矩形面积。对于长度为 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])
其中 h[i] 表示数组中的元素,min(h[i], h[i+1], ..., h[i+k-1]) 表示这 k 个相邻元素中的最小值。我们的目标是计算出对于每个 k,最大化 R(k),并返回其最大值。
二、问题分析
-
最大矩形面积的计算方式: 对于任意长度
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])
这个公式表达的是,在这
k个相邻元素中,我们以最小值作为矩形的高度,k作为矩形的宽度,计算该矩形的面积。 -
暴力解法: 对于每个
k(从 1 到N),我们可以遍历数组中所有长度为k的子数组,找到每个子数组的最小值并计算矩形面积。这样,我们可以得到一个包含所有k的最大矩形面积的集合。但暴力解法的时间复杂度较高,尤其是在
N较大的情况下,每次计算一个子数组的最小值需要O(k)时间,所以总体时间复杂度是 O(N^2),这对于大数据规模来说会显得非常慢。
三、单调队列与滑动窗口优化
滑动窗口 是一种常用的技巧,特别适合用于处理需要连续子数组(或子区间)问题的场景。在本题中,我们需要对于每个 k 计算连续的 k 个元素中的最小值,而滑动窗口技术可以帮助我们高效地实现这一点。
单调队列 是一种特殊的队列,它可以在 O(1) 的时间内得到当前窗口中的最小值或最大值。对于本题,我们可以使用单调队列来维护当前窗口中的最小值,队列中的元素按从小到大的顺序排列,这样队首元素就是当前窗口的最小值。
操作步骤:
- 当我们滑动窗口时,队列会自动保持队首是当前窗口的最小值。
- 每次窗口滑动时,我们移除队列头部不在当前窗口中的元素,并把新的元素加入队列,同时保持队列中的元素单调性。
通过使用单调队列,我们可以在 O(1) 的时间内得到当前窗口的最小值,从而使得每次窗口滑动的计算时间复杂度降到 O(1)。 from collections import deque
def solution(n, array): max_area = 0 # 记录最大矩形面积
# 枚举每个 k (1 <= k <= N)
for k in range(1, n + 1):
deq = deque() # 双端队列用于维护当前窗口的最小值索引
# 初始填充前 k 个元素的窗口
for i in range(k):
# 维护队列,使得队列中的元素按从小到大的顺序排列
while deq and array[deq[-1]] >= array[i]:
deq.pop()
deq.append(i)
# 计算第一个窗口的最大矩形面积
max_area = max(max_area, k * array[deq[0]])
# 滑动窗口,计算每个位置的矩形面积
for i in range(k, n):
# 移除不在窗口范围内的元素
while deq and deq[0] <= i - k:
deq.popleft()
# 插入新的元素,维护单调性
while deq and array[deq[-1]] >= array[i]:
deq.pop()
deq.append(i)
# 计算当前窗口的最大矩形面积
max_area = max(max_area, k * array[deq[0]])
return max_area
测试用例
if name == "main": print(solution(5, [1, 2, 3, 4, 5])) # Expected output: 9
五、详细解读
-
初始化:
max_area用于记录计算过程中遇到的最大矩形面积。deq是一个双端队列,用于高效地计算每个窗口内的最小值。
-
滑动窗口操作:
-
对于每个
k(从 1 到N),首先计算第一个长度为k的窗口的最小值,并更新max_area。 -
然后,滑动窗口,每次滑动时:
- 删除队列头部不在当前窗口内的元素。
- 添加新的元素,并保持队列中的元素按照从小到大的顺序排列,这样队首的元素就是当前窗口的最小值。
-
-
面积计算:
- 每次更新窗口时,计算当前窗口的矩形面积
k * array[deq[0]],并更新max_area。
- 每次更新窗口时,计算当前窗口的矩形面积
-
返回结果:
- 最终返回
max_area,即所有k长度窗口的最大矩形面积。
- 最终返回
七、总结
这道题考察了滑动窗口和单调队列的应用。通过双端队列,能够在 O(1) 的时间内计算每个窗口的最小值,从而避免了暴力解法中的高时间复杂度,使得问题能够更高效地解决。虽然最终的时间复杂度是 O(N^2),但在许多实际情况下,这种优化方式相较于暴力解法仍然能提供显著的性能提升。