题目解析:二分数字组合
小S正在分析一个数组 h_1, h_2, ..., h_N,数组的每个元素代表某种高度。小S感兴趣的是,当我们选取任意 k 个相邻元素时,如何计算它们所能形成的最大矩形面积。
对于 k 个相邻的元素,我们定义其矩形的最大面积为:
R(k) = k * min(h[i], h[i + 1], ..., h[i + k - 1])
即,R(k) 的值为这 k 个相邻元素中的最小值乘以 k。现在,小S希望你能帮他找出对于任意 k,R(k) 的最大值。
测试样例
样例1:
输入:
n = 5, array = [1, 2, 3, 4, 5]输出:9
样例2:
输入:
n = 6, array = [5, 4, 3, 2, 1, 6]输出:9
样例3:
输入:
n = 4, array = [4, 4, 4, 4]输出:16
问题理解
给定一个数组 array,我们需要找到任意 k 个相邻元素所能形成的最大矩形面积。具体来说,对于任意 k 个相邻元素,矩形的最大面积为这 k 个元素中的最小值乘以 k。我们需要找到所有可能的 k 中,最大的矩形面积。
数据结构与算法选择
-
暴力解法:
- 遍历所有可能的
k(从 1 到n)。 - 对于每个
k,遍历数组中所有可能的k个相邻元素,计算其矩形面积。 - 时间复杂度为
O(n^3),效率较低。
- 遍历所有可能的
-
优化解法:
- 使用单调栈(Monotonic Stack)来优化查找最小值的过程。
- 单调栈可以帮助我们在
O(n)时间内找到每个元素作为最小值时,能形成的最大矩形面积。
算法步骤
-
初始化:
- 使用一个栈来存储数组元素的索引。
- 初始化
max_area为 0,用于记录最大矩形面积。
-
遍历数组:
- 对于每个元素
array[i],如果当前元素大于或等于栈顶元素对应的值,则将其索引压入栈中。 - 如果当前元素小于栈顶元素对应的值,则弹出栈顶元素,并计算以该元素为最小值的矩形面积。
- 计算面积时,宽度为当前索引
i减去栈顶元素的索引(如果栈为空则为i)。
- 对于每个元素
-
处理剩余元素:
- 遍历结束后,栈中可能还有元素,依次弹出并计算以这些元素为最小值的矩形面积。
-
返回结果:
- 返回
max_area。
- 返回
代码框架
def solution(n, array):
stack = []
max_area = 0
index = 0
while index < n:
# If this bar is higher than the bar at stack top, push it to the stack
if not stack or array[index] >= array[stack[-1]]:
stack.append(index)
index += 1
else:
# Pop the top
top_of_stack = stack.pop()
# Calculate the area with array[top_of_stack] as the smallest (or minimum height) bar
area = (array[top_of_stack] *
((index - stack[-1] - 1) if stack else index))
# Update max area, if needed
max_area = max(max_area, area)
# Now pop the remaining bars from stack and calculate area
while stack:
top_of_stack = stack.pop()
area = (array[top_of_stack] *
((index - stack[-1] - 1) if stack else index))
max_area = max(max_area, area)
return max_area
关键步骤解释
-
栈的使用:
- 栈中存储的是数组元素的索引,这样可以方便地计算矩形的宽度。
- 当遇到一个比栈顶元素小的元素时,弹出栈顶元素并计算面积。
-
面积计算:
- 面积的计算公式为
array[top_of_stack] * width,其中width是当前索引index减去栈顶元素的索引(如果栈为空则为index)。
- 面积的计算公式为
-
处理剩余元素:
- 遍历结束后,栈中可能还有元素,这些元素对应的矩形面积也需要计算。
通过上述步骤,我们可以在 O(n) 时间内找到最大矩形面积。
心得
当前代码展示了如何使用单调栈(Monotonic Stack)来解决最大矩形面积的问题。通过分析和学习这段代码,你可以学到以下几个重要的编程和算法概念:
1. 单调栈(Monotonic Stack)
单调栈是一种特殊的栈结构,栈中的元素保持单调递增或单调递减的顺序。在这段代码中,单调栈用于在 O(n) 时间内找到每个元素作为最小值时,能形成的最大矩形面积。
-
栈的基本操作:
stack.append(index):将元素索引压入栈中。stack.pop():弹出栈顶元素。stack[-1]:访问栈顶元素。
-
单调栈的应用:
- 通过维护一个单调递增的栈,可以在常数时间内找到每个元素的下一个更小元素。
- 在弹出栈顶元素时,计算以该元素为最小值的矩形面积。
2. 时间复杂度优化
通过使用单调栈,代码将时间复杂度从 O(n^3) 优化到 O(n),极大地提高了算法的效率。
- 遍历数组:
- 每个元素最多入栈和出栈一次,因此总的时间复杂度为
O(n)。
- 每个元素最多入栈和出栈一次,因此总的时间复杂度为
3. 边界条件处理
代码在处理边界条件时表现良好,例如当栈为空时,计算面积的宽度为当前索引 index。
- 边界检查:
- 在计算面积时,使用
(index - stack[-1] - 1) if stack else index来处理栈为空的情况。
- 在计算面积时,使用
4. 代码结构与可读性
代码结构清晰,逻辑流程明确,注释详细,有助于理解和维护代码。
-
变量命名:
- 变量命名如
stack、max_area、index等都很直观,有助于理解代码的逻辑。
- 变量命名如
-
注释:
- 代码中添加了详细的注释,解释了每一步的操作,这对于理解和维护代码非常有帮助。
5. 测试用例
代码中包含了一个测试用例,用于验证代码的正确性。通过添加更多的测试用例,可以进一步验证代码的鲁棒性。
- 测试用例设计:
- 通过不同的输入数据,验证代码在各种情况下的表现。
6. 代码复用性
可以将计算面积的部分提取为一个单独的函数,以提高代码的复用性和可读性。
- 函数提取:
- 将计算面积的逻辑提取为
calculate_area函数,使代码更加模块化。
- 将计算面积的逻辑提取为
总结
通过学习这段代码,我可以掌握单调栈的应用、时间复杂度优化、边界条件处理、代码结构与可读性、测试用例设计以及代码复用性等重要的编程和算法概念。这些知识不仅有助于解决类似的最大矩形面积问题,还能提升我的整体编程能力。