问题分析
问题背景
小S有一个数组 ℎ=[ℎ1,ℎ2,…,ℎ𝑁],每个元素 ℎ𝑖 代表某种高度。小S对这些高度感兴趣的是,当我们在数组中选取任意 𝑘 个相邻元素时,如何计算它们所能形成的最大矩形面积。
问题定义
对于任意 𝑘 个相邻的元素,我们定义其矩形的最大面积为: 𝑅(𝑘)=𝑘×min(ℎ[𝑖],ℎ[𝑖+1],…,ℎ[𝑖+𝑘−1])
即,𝑅(𝑘) 的值为这 𝑘 个相邻元素中的最小值乘以 𝑘。现在,小S希望你能帮他找出对于任意 k,𝑅(𝑘) 的最大值。
输入
- 一个整数 𝑁,表示数组的长度。
- 一个整数数组 ℎ,长度为 𝑁。
输出
- 一个整数,表示对于任意 𝑘,𝑅(𝑘) 的最大值。
解决思路
-
初始化变量:
- 初始化一个变量
max_area为 0,用于存储最终的最大面积。 - 初始化一个单调栈
stack用于计算每个元素左边和右边第一个小于它的元素的位置。 - 初始化两个数组
left和right,分别存储每个元素左边和右边第一个小于它的元素的位置。
- 初始化一个变量
-
计算左边界:
- 使用单调栈从左到右遍历数组,计算每个元素左边第一个小于它的元素的位置,并存储在
left数组中。
- 使用单调栈从左到右遍历数组,计算每个元素左边第一个小于它的元素的位置,并存储在
-
计算右边界:
- 使用单调栈从右到左遍历数组,计算每个元素右边第一个小于它的元素的位置,并存储在
right数组中。
- 使用单调栈从右到左遍历数组,计算每个元素右边第一个小于它的元素的位置,并存储在
-
计算最大面积:
- 遍历数组,对于每个元素,计算以其为最小值的矩形的宽度
width,并计算面积area。 - 更新
max_area,记录当前的最大面积。
- 遍历数组,对于每个元素,计算以其为最小值的矩形的宽度
具体步骤
-
初始化变量:
max_area = 0stack = []left = [0] * nright = [0] * n
-
计算左边界:
-
遍历数组,对于每个元素
i:- 使用单调栈找到左边第一个小于
array[i]的元素的位置。 - 将位置存储在
left[i]中。 - 将当前索引
i压入栈中。
- 使用单调栈找到左边第一个小于
-
-
计算右边界:
-
清空栈。
-
从右到左遍历数组,对于每个元素
i:- 使用单调栈找到右边第一个小于
array[i]的元素的位置。 - 将位置存储在
right[i]中。 - 将当前索引
i压入栈中。
- 使用单调栈找到右边第一个小于
-
-
计算最大面积:
-
遍历数组,对于每个元素
i:- 计算宽度
width = right[i] - left[i] - 1。 - 计算面积
area = array[i] * width。 - 更新
max_area,记录当前的最大面积。
- 计算宽度
-
-
返回结果:
- 返回
max_area。
- 返回
代码实现
def solution(n, array):
# Edit your code here
# 初始化单调栈和左右边界数组
stack = []
left = [0] * n
right = [0] * n
# 计算左边界
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)
# 清空栈
stack = []
# 计算右边界
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)
# 计算最大面积
max_area = 0
for i in range(n):
width = right[i] - left[i] - 1
max_area = max(max_area, array[i] * width)
return max_area
if __name__ == "__main__":
# Add your test cases here
print(solution(5, [1, 2, 3, 4, 5]) == 9)
总结
通过解决这道关于计算最大矩形面积的问题,我深刻体会到了算法优化的重要性。最初,我尝试使用暴力解法,即枚举所有可能的 𝑘 值并计算每个子数组的最小值,但这种方法的时间复杂度高达 𝑂(𝑁2),在大数据量下显然不可行。因此,我转向了更高效的解决方案。使用单调栈来预处理每个元素的左右边界,这一技巧极大地提高了算法的效率。通过两次遍历数组,分别计算每个元素左边和右边第一个小于它的元素的位置,可以在 𝑂(𝑁) 的时间复杂度内完成预处理。这一过程不仅简化了后续的计算,还使得整体算法更加清晰和高效。在实际编码过程中,我也遇到了一些细节问题,比如边界条件的处理和栈的清空操作。通过调试和测试,我逐渐完善了代码,最终得到了正确的结果。这让我意识到,编写高效的算法不仅仅是理论上的设计,还需要在实践中不断调试和优化。