给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。
本题可使用双指针法或者单调栈法来解决。
(1)双指针法:
双指针法的核心思想是逐列计算可以储存的雨水量,而每一列雨水的高度取决于,该列 左侧最高的柱子和右侧最高的柱子中最矮的那个柱子的高度。即: min(lHeight, rHeight) - height。
我们把每一个位置的左边最高高度记录在一个数组上(maxLeft),右边最高高度记录在一个数组上(maxRight),这样就避免了重复计算。当前位置,左边的最高高度是前一个位置的左边最高高度和本高度的最大值。
即从左向右遍历:maxLeft[i] = max(height[i], maxLeft[i - 1]);
从右向左遍历:maxRight[i] = max(height[i], maxRight[i + 1]);
代码如下:
class Solution {
public:
int trap(vector<int>& height) {
int len = height.size();
if(len <= 2) return 0;
vector<int> maxLeft(len, 0);
vector<int> maxRight(len, 0);
maxLeft[0] = height[0];
for(int i = 1; i < len; i++) {
maxLeft[i] = max(height[i], maxLeft[i - 1]);
}
maxRight[len - 1] = height[len - 1];
for(int j = len - 2; j >= 0; j--) {
maxRight[j] = max(height[j], maxRight[j + 1]);
}
int sum = 0;
for(int i = 1; i < len - 1; i++) {
int cur = min(maxRight[i], maxLeft[i]) - height[i];
if(cur > 0) sum += cur;
}
return sum;
}
};
(2)单调栈法
1.单调栈法是采用逐行计算来计算能储存雨水的最大量
2.单调栈内元素的顺序
从栈头(元素从栈头弹出)到栈底的顺序应该是从小到大的顺序。
因为一旦发现添加的柱子高度大于栈头元素了,此时就出现凹槽了,栈头元素就是凹槽底部的柱子,栈头第二个元素就是凹槽左边的柱子,而添加的元素就是凹槽右边的柱子。
3.遇到相同高度的柱子怎么办
遇到相同的元素,更新栈内下标,就是将栈里元素(旧下标)弹出,将新元素(新下标)加入栈中。
因为我们要求宽度的时候 如果遇到相同高度的柱子,需要使用最右边的柱子来计算宽度。
4.栈里要保存什么数值
使用单调栈,也是通过 长 * 宽 来计算雨水面积的。
长就是通过柱子的高度来计算,宽是通过柱子之间的下标来计算,因此栈内记录下标就可以了。
代码如下:
class Solution {
public:
int trap(vector<int>& height) {
if (height.size() <= 2) return 0; // 可以不加
stack<int> st; // 存着下标,计算的时候用下标对应的柱子高度
st.push(0);
int sum = 0;
for (int i = 1; i < height.size(); i++) {
if (height[i] < height[st.top()]) { // 情况一
st.push(i);
} if (height[i] == height[st.top()]) { // 情况二
st.pop(); // 其实这一句可以不加,效果是一样的,但处理相同的情况的思路却变了。
st.push(i);
} else { // 情况三
while (!st.empty() && height[i] > height[st.top()]) { // 注意这里是while
int mid = st.top();
st.pop();
if (!st.empty()) {
int h = min(height[st.top()], height[i]) - height[mid];
int w = i - st.top() - 1; // 注意减一,只求中间宽度
sum += h * w;
}
}
st.push(i);
}
}
return sum;
}
};