接雨水
题目描述
给定 n
个非负整数表示每个宽度为 1
的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。
示例 1:
输入:height = [0,1,0,2,1,0,1,3,2,1,2,1]
输出:6
解释:上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图,在这种情况下,可以接 6 个单位的雨水(蓝色部分表示雨水)。
示例 2:
输入:height = [4,2,0,3,2,5]
输出:9
解题思路
本题可以看做在第i根柱子左右两边分别找到一根比自身高的柱子,然后计算凹槽的面积,因此当前问题就可以使用单调栈进行解决了。
具体步骤如下:
-
初始化:
- 创建一个栈,用于存储数组的索引。
- 初始化结果变量
ans
为0。
-
遍历数组:
-
对于数组中的每一个元素,执行以下操作:
- 如果栈不为空且当前元素的高度大于栈顶元素的高度,进入处理栈中元素的逻辑。
- 否则,直接将当前元素的索引压入栈中。
-
-
处理栈中元素:
- 弹出栈顶元素,这个元素是当前考虑的“中间”柱子。
- 计算可以接的水的高度,这个高度是当前柱子和新的栈顶元素中较小的那个高度减去中间柱子的高度。
- 检查栈是否为空,如果不为空,计算当前柱子和新的栈顶元素之间的水平距离(宽度)。
- 计算出的水量累加到结果中。
-
更新栈:
- 将当前元素的索引压入栈中,继续维护单调栈的性质。
代码实现
测试地址:https://leetcode.cn/problems/trapping-rain-water
class Solution {
public:
int trap(vector<int> &height) {
int n = height.size(); // 获取输入数组的长度
int ans = 0; // 初始化结果变量,用于存储最终的积水量
stack<int> st; // 创建一个栈,用于存储数组的索引
// 遍历数组中的每一个元素
for (int i = 0; i < n; i++) {
// 当栈不为空且当前元素的高度大于栈顶元素的高度时
while (!st.empty() && height[i] > height[st.top()]) {
int mid = st.top(); // 获取栈顶元素的索引
st.pop(); // 弹出栈顶元素
if (!st.empty()) { // 确保栈不为空
int h = min(height[i], height[st.top()]) - height[mid]; // 计算可以积水的有效高度
int w = i - st.top() - 1; // 计算可以积水的宽度
ans += h * w; // 累加积水量
}
}
st.push(i); // 将当前元素的索引压入栈中
}
return ans; // 返回最终的积水量
}
};
柱状图中最大的矩形
题目描述
给定 n 个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1 。
求在该柱状图中,能够勾勒出来的矩形的最大面积。
示例 1:
输入:heights = [2,1,5,6,2,3]
输出:10
解释:最大的矩形为图中红色区域,面积为 10
示例 2:
输入: heights = [2,4]
输出: 4
解题思路
本题和((20240718134953-gyv3uh2 ‘接雨水’))的解题思路类似,也是可以用单调栈的思路进行解题的,区别是接雨水是维护从栈顶到栈底单调递增的序列,而本题因为需要从最左右两边找到比当前元素更小的值,因此需要维护的是单调递减的序列。
具体解题步骤如下:
-
处理边界条件
- 为了方便处理边界情况,我们在柱状图的高度数组
heights
的前后各添加一个高度为 0 的柱子。这样可以确保在遍历过程中栈不会为空,并且能够正确计算以最左和最右柱子为边界的矩形面积。
- 为了方便处理边界情况,我们在柱状图的高度数组
-
初始化变量
- 我们需要一个栈
st
来存储柱子的索引,ans
来存储计算出的最大矩形面积,n
来存储处理后的高度数组的大小。
- 我们需要一个栈
-
遍历高度数组
-
当前柱子高度大于栈顶柱子高度:
- 将当前柱子的索引压入栈中。
- 这是因为当前柱子可以作为一个新的可能的矩形的右边界,而栈中的柱子高度是递减的,能形成一个递减的序列。
-
当前柱子高度小于栈顶柱子高度:
- 弹出栈顶元素,将其高度作为当前矩形的高度。
- 计算以这个高度为高的矩形面积,并更新最大面积。
- 为了计算这个矩形的宽度,我们需要栈顶元素的左边界和当前柱子的索引作为右边界。
-
-
计算矩形面积,获取最大的矩形面积
- 弹出栈顶元素
mid
,这是当前考虑的矩形的高度索引。 - 获取新的栈顶元素
left
,这是矩形的左边界索引。 - 当前索引
i
是矩形的右边界。 - 计算矩形的宽度
w = right - left - 1
,其中right
是i
,left
是新的栈顶元素。 - 计算矩形的面积
w * h
,其中h
是heights[mid]
。 - 更新最大面积
ans = max(ans, w * h)
。
- 弹出栈顶元素
代码实现
测试地址:https://leetcode.cn/problems/largest-rectangle-in-histogram/
class Solution {
public:
int largestRectangleArea(vector<int> &heights) {
// 在数组前后各加一个0,方便处理边界情况
heights.push_back(0);
heights.insert(heights.begin(), 0);
int n = heights.size();
int ans = 0;
stack<int> st;
for (int i = 0; i < n; i++) {
// 当当前高度小于栈顶高度时,计算以栈顶高度为高的矩形面积
while (!st.empty() && heights[i] < heights[st.top()]) {
int mid = st.top(); // 获取栈顶元素索引,即当前矩形的高度
st.pop(); // 弹出栈顶元素
int left = st.top(); // 获取新的栈顶元素索引,即左边界
int right = i; // 当前索引为右边界
int w = right - left - 1; // 计算矩形的宽度
int h = heights[mid]; // 获取矩形的高度
ans = max(ans, w * h); // 更新最大面积
}
st.push(i); // 将当前索引压入栈中
}
return ans; // 返回最大面积
}
};