2021-04-02

面试题 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;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值