题目描述:一个数组,针对任意 k 个相邻元素,计算它们所能形成的最大矩形面积。矩形的面积定义为选定的 k 个元素的最小值乘以 k。
思路分析
-
定义问题: 对于任意
k个相邻的元素h[i], h[i+1], ..., h[i+k-1],其矩形的最大面积R(k)计算公式为:R(k)=k×min(h[i],h[i+1],...,h[i+k−1])
我们的目标是找到在所有可能的
k的值下,R(k)的最大值。 -
暴力解法的时间复杂度: 暴力解法需要计算每个
k对应的最大矩形面积。对于每个k,我们需要在数组中检查每个长度为k的子数组,计算最小值。这导致暴力解法的时间复杂度是 O(N^2)。
时间复杂度优化
这个问题的核心是要计算给定数组中每个元素所能形成的最大矩形面积,具体地,对于每个元素 h[i],我们需要找到它作为矩形高度时,可以延展的最宽的范围。这个范围由两个因素决定:
- 左边界:在这个范围内,所有元素的高度都不小于
h[i],即向左扩展时,遇到比h[i]小的元素就停止。 - 右边界:在这个范围内,所有元素的高度也不小于
h[i],即向右扩展时,遇到比h[i]小的元素就停止。
1. 解法的关键思路:
为了高效地计算每个元素的最大矩形面积,我们可以通过以下步骤来解决:
a. 计算左边界(l[i])和右边界(r[i])
- 左边界:
l[i]表示在索引i处的元素h[i]向左可以扩展的最大范围。具体来说,l[i]是满足条件h[l[i]] >= h[i]的最远左边界。可以通过维护一个单调栈来实现。 - 右边界:
r[i]表示在索引i处的元素h[i]向右可以扩展的最大范围。计算方式与左边界相似,但方向是向右延伸。
b. 计算矩形面积
- 对于每个元素
h[i],矩形的宽度是r[i] - l[i] + 1,矩形的高度是h[i]。因此,矩形的面积是h[i] * (r[i] - l[i] + 1)。我们遍历所有元素,计算对应的面积,并找出最大面积。
2. 单调栈的具体操作:
a. 计算左边界 l[i] :
- 我们从左到右遍历数组
h,对于每个元素h[i],我们检查栈顶的元素(也就是比h[i]高的元素),如果栈顶元素比h[i]大或者相等,那么我们可以确定当前元素h[i]的左边界即栈顶元素的下标加1。 - 如果栈顶元素比
h[i]小,我们就弹出栈顶元素,并继续检查栈顶元素,直到找到一个更大的元素为止。
b. 计算右边界 r[i] :
- 右边界的计算过程与左边界类似,只是我们反转数组进行从右到左的遍历。
- 使用相同的单调栈的方法来计算每个元素的右边界。
3. 代码解析:
下面是代码中的核心逻辑:
def solution(n, array):
l, r = [0] * n, [n-1] * n # 初始化左边界和右边界
stk = [0] * n # 栈,用于存储索引
top = -1 # 栈的栈顶指针
# 计算左边界
for i in range(n):
while top >= 0 and array[i] <= array[stk[top]]:
top -= 1
if top >= 0:
l[i] = stk[top] + 1 # 左边界是栈顶元素的下标 + 1
top += 1
stk[top] = i # 将当前索引入栈
stk = [0] * n # 重置栈
top = -1
array.reverse() # 反转数组来计算右边界
# 计算右边界
for i in range(n):
while top >= 0 and array[i] <= array[stk[top]]:
top -= 1
if top >= 0:
r[i] = n - stk[top] - 2 # 右边界是栈顶元素的下标的反转位置
top += 1
stk[top] = i # 将当前索引入栈
ans = 0
r.reverse() # 反转右边界数组回到原顺序
array.reverse() # 反转原数组回到原顺序
# 计算最大矩形面积
for i in range(n):
ans = max(ans, (r[i] - l[i] + 1) * array[i]) # 计算矩形面积并更新最大值
return ans
左边界的计算:
- 对于每个
i,我们首先检查栈顶元素是否大于等于array[i]。如果是,则栈顶元素已经不再能扩展到更大的矩形,因此需要弹出栈顶元素。 - 弹出后,栈顶的元素是当前元素的左边界。如果栈为空,表示左边界可以扩展到数组的最左边,设置
l[i] = 0。
右边界的计算:
- 类似地,我们反转数组,然后按相同的方式计算每个元素的右边界。
r[i]是右边界的计算结果,它表示当前元素能够扩展的最远右边界。 - 由于数组已经反转,计算出的
r[i]是从右到左的边界,因此我们在最后需要将其反转回来。
最终面积计算:
- 对于每个元素
h[i],通过公式R(i) = h[i] * (r[i] - l[i] + 1)计算面积,并更新最大值。
5. 时间复杂度分析:
- 计算左边界:每个元素最多进栈一次,出栈一次,时间复杂度是 O(N)。
- 计算右边界:反转数组后,类似地每个元素最多进栈一次,出栈一次,时间复杂度是 O(N)。
- 计算面积:每个元素只访问一次,时间复杂度是 O(N)。
- 总时间复杂度:O(N)