给定一个直方图(也称柱状图),假设有人从上面源源不断地倒水,最后直方图能存多少水量?直方图的宽度为 1。
上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1]
表示的直方图,在这种情况下,可以接 6
个单位的水(蓝色部分表示水)。 感谢 Marcos 贡献此图。
示例:
输入: [0,1,0,2,1,0,1,3,2,1,2,1]
输出: 6
解答
位置i
处的水量由左右两侧最高处其中的较小值决定,因此可以使用双指针法,此外记录left
指针左侧的最大值left_max
,以及right
指针右侧的最大值right_max
:
class Solution {
public:
int trap(vector<int>& height) {
if(height.empty())
return 0;
int left = 0, right = height.size() - 1;
int left_max = 0, right_max = 0;
int result = 0;
while(left < right){
left_max = max(left_max, height[left]);
right_max = max(right_max, height[right]);
// 由左边的短板决定
if(height[left] < height[right]){
result += (left_max - height[left]);
left++;
}
// 由右边的短板决定
else{
result += (right_max - height[right]);
right--;
}
}
return result;
}
};
单调栈,存储位置索引,从栈底到栈顶高度依次递减,从左到右遍历,每遍历到一个比栈顶高的元素,就说明可以以栈顶元素为底,栈顶下一个元素(单调栈,一定是左侧更高的位置)和当前遍历元素为左右边界,构成一个可以盛水的矩形:
class Solution {
public:
int trap(vector<int>& height) {
if(height.empty())
return 0;
stack<int> s;
int result = 0;
for(int i = 0; i < height.size(); i++){
while(!s.empty() && height[s.top()] < height[i]){
int mid = s.top();
s.pop();
if(s.empty())
break;
int left = s.top();
int w = i - left - 1;
int h = min(height[i], height[left]) - height[mid];
result += (w * h);
}
s.push(i);
}
return result;
}
};