【随想录】-【11 单调栈】-【04】42 接雨水

重点:重点在于按照列算还是按照行算!!

(1)双指针:按每一列的雨水量算。O(n^2)时间复杂度。超时。(列算)

(2)动态规划:优化版双指针,先算出左右柱子最大高度,然后直接调用,复杂度O(n)

(3)单调栈复杂度O(n),难理解。建立动规。(行算)

42. 接雨水

难度困难

给定 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

提示:

  • n == height.length
  • 1 <= n <= 2 * 104
  • 0 <= height[i] <= 105

解析:

方法1:双指针(暴力搜索)

按照每一列的雨水量来算,那么遍历柱子。对于当前的柱子i,找到左右两边最高的柱子,那么当前柱子能存的与水量为min(h1, h2)-height[i]。注意第一个柱子和最后一个柱子不接水。但过不了,超时。

方法2:动态规划

双指针方法每次都找左右最低的柱子,这样重复计算。可以利用动态规划首先计算出每个柱子左右最低柱子高度。这样复杂度O(n)

方法3:单调栈

单调栈问题,按照行算

首先,依次读入数组中的元素,前面若干个0直接去掉,直到读到一个大于0的,将其入栈。

对于当前的柱子高度height[i]和栈顶元素柱子高度top,同样有两种情况:

(1)当前柱子高度 <=栈顶元素柱子高度:直接入栈;

(2)当前柱子高度 =栈顶元素柱子高度:出栈栈顶,然后入栈;

(2)当前柱子高度 > 栈顶元素柱子高度:栈顶元素依次出栈,同时算出面积;

第二种情况需要注意。如果当前栈顶元素跟入栈元素一样大,那么前面那个柱子就没用了。

需要注意,栈内的元素肯定是从栈顶到栈底递增,即栈底的最大。

详细注解见:代码随想录 (programmercarl.com)

代码:

class Solution {
public:
    int trap(vector<int>& height) {
        if (height.size() <= 2) return 0;
        vector<int> maxLeft(height.size(), 0);
        vector<int> maxRight(height.size(), 0);
        int num=0;

        // 记录每个柱子左边柱子最大高度
        for(int i=1;i<height.size();i++)
            maxLeft[i]=max(maxLeft[i-1],height[i-1]);
        // 记录每个柱子右边柱子最大高度
        for(int i=height.size()-2;i>=0;i--)
            maxRight[i]=max(maxRight[i+1],height[i+1]);

        //第一根柱子和最后一根不接水
        //计算方法:左边最高的和右边最高的的最小值,减去当前柱子高度,宽度为1
        //按列算,宽度默认为1,只算高度
        for(int i=1;i<height.size()-1;i++){
            int tmp=min(maxLeft[i],maxRight[i])-height[i];
            if(tmp>0)
                num+=tmp;
        }
        return num;
    }
};
class Solution {
public:
    int trap(vector<int>& height) {
        if (height.size() <= 2) return 0;
        stack<int> stk;
        int sum=0;
        while(!stk.empty())
            stk.pop();
        stk.push(0);
        //三种情况
        for(int j=1;j<height.size();j++){
            //当前元素小于栈顶,直接入栈
            if(height[j]<height[stk.top()])
                stk.push(j);
            
            //当前元素等于栈顶,出栈栈顶再入栈
            else if(height[j]==height[stk.top()]){
                stk.pop();
                stk.push(j);
            }
            //当前元素小于栈顶,算面积
            else{
                while(!stk.empty()&&height[j]>height[stk.top()]){
                    int mid=stk.top();
                    stk.pop();
                    if(!stk.empty()){
                        int h = min(height[stk.top()], height[j]) - height[mid];
                        int w = j - stk.top() - 1; // 注意减一,只求中间宽度
                        sum += h * w;
                    }
                }
                stk.push(j);
                //此处有个小重点,可以看到如果栈内只有一个元素的话,出栈之后栈为空了,取不到左边凹槽
                //那个元素就直接被出栈,没派上用场,因为新入栈的当前元素比它高,挡住了它,它就没用了
            }
        }
        return sum;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值