剑指offer 接雨水(单调栈、前缀和、双指针)

题目链接

题目描述:

给定 n n n 个非负整数表示每个宽度为 1 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 个单位的雨水(蓝色部分表示雨水)。

示例二:

输入:height = [4,2,0,3,2,5]
输出:9

思路一:单调栈

这种柱状图类似的题,往往用单调栈。在栈里面维护一个单调下降的序列,

  1. 若是遇到比栈顶元素更小的,根据栈顶元素计算它的 ”贡献“ ,然后直接入栈。
  2. 若是遇到比栈顶元素更大的,不断弹出栈顶元素,同时计算 ”贡献“,直到符合条件 1 时,按上一条方法计算。
//单调栈
class Solution {
public:
    int trap(vector<int>& h) {
        const int N=2e4+10;
        int st[N],tot=0,ans=0;
        for(int i=0;i<h.size();i++){
            int pre=0;
            while(tot&&h[i]>=h[st[tot-1]]){
                ans+=(h[st[tot-1]]-pre)*(i-st[tot-1]-1);	
                pre=h[st[tot-1]];
                tot--;
            }
            if(tot&&h[i]<h[st[tot-1]]){
                ans+=(h[i]-pre)*(i-st[tot-1]-1);
            }	
            st[tot++]=i;
        }
        return ans;
    }
};

思路二:前缀和

设, h l i hl_i hli 为第 i i i 列左边最高的墙, h r i hr_i hri 为第 i i i 列右边最高的墙。

计算某一列中能储水的高度,需要知道 m i n ( h l i , h r i ) min(hl_i,hr_i) min(hli,hri)

可以用前缀和先预处理, O ( n ) O(n) O(n) 求出所有的 h l i hl_i hli h r i hr_i hri ,最后再 O ( n ) O(n) O(n) 求出答案。

//前缀后缀
class Solution {
public:
    int trap(vector<int>& h) {
        int n=h.size(),ans=0;
        vector<int> l(n),r(n);
        l[0]=r[0]=h[0];
	    l[n-1]=r[n-1]=h[n-1];
        for(int i=1;i<=n-2;i++){
            l[i]=max(l[i-1],h[i]);
            r[n-1-i]=max(r[n-i],h[n-1-i]);
        }
        for(int i=1;i<=n-2;i++){
            int x=min(l[i-1],r[i+1]);
            if(x>h[i]) ans+=x-h[i];
        }
        return ans;
    }
};

思路三:双指针

在思路二的基础上,要计算第 i i i 列的储水量,其实不需要知道 h l i hl_i hli h r i hr_i hri,只需知道 m i n ( h l i , h r i ) min(hl_i,hr_i) min(hli,hri) 即可。

两个指针初始分别指向最左边和最右边,每次移动 ”最高的墙更低一些“ 那个方向的指针,遍历一遍即可。

//双指针
class Solution {
public:
    int trap(vector<int>& h) {
        int l=0,r=h.size()-1,hl=h[l],hr=h[r],ans=0;
        while(l<r){
            while(l<r&&hl<=hr){
                hl=max(hl,h[++l]);
                int x=min(hl,hr);
                if(x>h[l]) ans+=x-h[l];
            }
            while(l<r&&hl>hr){
                hr=max(hr,h[--r]);
                int x=min(hl,hr);
                if(x>h[r]) ans+=x-h[r];
            }
        }
        return ans;
    }
};

复杂度:

时间复杂度都是 O ( n ) O(n) O(n)。双指针的空间复杂度 O ( 1 ) O(1) O(1),其他两个空间复杂度 O ( n ) O(n) O(n)

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

m0_51864047

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值