面试题 17.21. 直方图的水量
题目
给定一个直方图(也称柱状图),假设有人从上面源源不断地倒水,最后直方图能存多少水量?直方图的宽度为 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
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/volume-of-histogram-lcci
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
代码
class Solution {
public:
int trap(std::vector<int>& height) {
//方法一:计算每一层的体积容量,将其总容量加起来之后减去柱子的体积,就可以获得雨水的体积
// 按行计算
/*
int Sum = accumulate(height.begin(), height.end(), 0); // 得到柱子的体积
int volume = 0; // 总体积和高度初始化
int high = 1;
int size = height.size();
int left = 0; // 双指针初始化
int right = size - 1;
while (left <= right) {
while (left <= right && height[left] < high) {
left++;
}
while (left <= right && height[right] < high) {
right--;
}
volume += right - left + 1; // 每一层的容量都加起来
high++; // 高度加一
}
return volume - Sum; // 总体积减去柱子体积,即雨水总量
*/
//方法二:暴力解,计算每一根柱子的左右最高的柱子高,获得其较小的值之后减去该柱子的体积,就可以获得该柱子的课接雨水值
//方法二三是按照列计算
/*
int sum=0;
int temp=0;
int size=height.size();
for(int i=0;i<size;i++){
if(i==0||i==size-1) continue;//柱子左右边界不可以存雨水
int leftMax=0,rightMax=0;
for(int left=0;left<i;left++){
leftMax=max(leftMax,height[left]);
}
for(int right=i+1;right<size;right++){
rightMax=max(rightMax,height[right]);
}
temp=min(rightMax,leftMax)-height[i];
if(temp>0) sum+=temp;
}
return sum;
*/
//方法三:对于方法二的优化,动态规划的计算每一个位置上的左边和右边的最大值,可以降低时间复杂度,降低为O(logn)
/*
int size=height.size();
if(size==0) return 0;
vector<int> leftMax(size,0),rightMax(size,0);
//获得左边大值
leftMax[0]=height[0];
for(int i=1;i<size;i++){
leftMax[i]=max(leftMax[i-1],height[i]);
}
//获得右边大值
rightMax[size-1]=height[size-1];
for(int i=size-2;i>=0;i--){
rightMax[i]=max(rightMax[i+1],height[i]);
}
//计算结果
int sum=0;
int temp=0;
for(int i=0;i<size;i++){
if(i==0||i==size-1) continue;
temp=min(rightMax[i],leftMax[i])-height[i];
if(temp>0) sum+=temp;
}
return sum;
*/
//方法四:使用单调栈
//按行计算
/*
单调栈内部从栈顶到栈低,按照从小到大的顺序排列,则当push的位置的元素大于top的位置元素时,
该位置元素就是top位置元素右边的柱子,top位置元素的下边位置的元素就是top左边的元素,
三种情况:
1.push>top 可以加水
2.push==top 直接弹出top,压入push,更换元素位置,目的是求宽度的时候,如果遇到相同高度的柱子,需要使用最右边的柱子来计算宽度。
3.push<top push
*/
if(height.size()<=2) return 0;
int size=height.size();
stack<int> pos;
pos.push(0);
int sum=0;
for(int i=1;i<size;i++){
if(height[i]<height[pos.top()]){
pos.push(i);
}
else if(height[i]==height[pos.top()]){
pos.pop();
pos.push(i);
}
else{
while(!pos.empty()&&height[i]>height[pos.top()]){
int middle=pos.top();
pos.pop();
if(!pos.empty()){
int left=pos.top();
int h=min(height[left],height[i])-height[middle];
sum+=h*(i-left-1);
}
}
pos.push(i);
}
}
return sum;
}
};