问题描述
小S最近在分析一个数组 ,数组的每个元素代表某种高度。小S对这些高度感兴趣的是,当我们选取任意 个相邻元素时,如何计算它们所能形成的最大矩形面积。
对于 个相邻的元素,我们定义其矩形的最大面积为:
即, 的值为这 个相邻元素中的最小值乘以 。现在,小S希望你能帮他找出对于任意 , 的最大值。
题目分析
问题理解
题目要求我们计算一个数组中任意 k 个相邻元素所能形成的最大矩形面积。具体来说,对于任意 k 个相邻元素,矩形的最大面积定义为这 k 个元素中的最小值乘以 k。我们需要找出对于所有可能的 k,这个最大面积的最大值。
数据结构选择
为了高效地解决这个问题,我们可以借鉴经典的“最大矩形面积”问题(通常使用单调栈来解决)。单调栈是一种非常适合处理“下一个更大/更小元素”问题的数据结构。在这里,我们可以利用单调栈来维护一个递增的高度序列,从而在 O(n) 的时间复杂度内计算出最大矩形面积。
算法步骤
-
初始化单调栈:使用一个栈来存储数组元素的索引,栈中的元素保持递增顺序。
-
遍历数组:对于数组中的每个元素,进行以下操作:
- 如果当前元素小于栈顶元素对应的值,说明栈顶元素的高度不能再向右扩展,因此我们可以计算以栈顶元素为高度的矩形面积。
- 计算面积时,宽度为当前元素索引与栈顶元素前一个元素索引的差值。
- 更新最大面积。
- 将当前元素的索引压入栈中。
-
处理栈中剩余元素:遍历结束后,栈中可能还剩下一些元素,这些元素的高度可以一直扩展到数组的末尾。我们依次弹出栈顶元素,计算以该元素为高度的矩形面积,并更新最大面积。
-
返回最大面积:最终,我们返回计算得到的最大面积。
详细分析
-
单调栈的作用:
- 单调栈可以帮助我们快速找到每个元素作为最小值时,能够形成的最大矩形的宽度。
- 通过维护一个递增的栈,我们可以确保栈中的每个元素在弹出时,其右侧的元素都大于等于它,从而可以计算出以该元素为高度的矩形面积。
-
计算面积的逻辑:
- 当栈顶元素的高度大于当前元素时,栈顶元素的高度不能再向右扩展,因此我们可以计算以栈顶元素为高度的矩形面积。
- 宽度为当前元素索引与栈顶元素前一个元素索引的差值。如果栈为空,说明栈顶元素的高度可以一直扩展到数组的起始位置。
-
时间复杂度:
- 每个元素最多入栈和出栈一次,因此时间复杂度为
O(n)。
- 每个元素最多入栈和出栈一次,因此时间复杂度为
-
空间复杂度:
- 使用了一个栈来存储元素的索引,因此空间复杂度为
O(n)。
- 使用了一个栈来存储元素的索引,因此空间复杂度为
代码实现思路
- 初始化栈和最大面积变量。
- 遍历数组,对于每个元素:
- 如果栈不为空且当前元素小于栈顶元素对应的值,弹出栈顶元素并计算面积。
- 将当前元素的索引压入栈。
- 处理栈中剩余元素,计算面积并更新最大面积。
- 返回最大面积。
使用单调栈可以高效地解决这个问题,并且在 O(n) 的时间复杂度内完成计算。
代码如下:
int solution(int n, std::vector<int>& array) {
std::stack<int> stack;
int max_area = 0;
// 遍历数组
for (int i = 0; i < n; ++i) {
// 当栈不为空且当前元素小于栈顶元素对应的值时
while (!stack.empty() && array[i] < array[stack.top()]) {
// 弹出栈顶元素
int height = array[stack.top()];
stack.pop();
// 计算宽度
int width = stack.empty() ? i : i - stack.top() - 1;
// 计算面积并更新最大面积
max_area = std::max(max_area, height * width);
}
// 将当前元素的索引压入栈
stack.push(i);
}
// 处理栈中剩余的元素
while (!stack.empty()) {
int height = array[stack.top()];
stack.pop();
int width = stack.empty() ? n : n - stack.top() - 1;
max_area = std::max(max_area, height * width);
}
return max_area;
}