LeetCode接雨水i 两种思想四种解法。

法一:以一个柱子为视角,一块地方有雨水等价于后面有一个的柱子的高度大于等于本身的高度。找到它,然后细细调试即可。用的是单调栈的思想。

class Solution {
public:

    int trap(vector<int>& height) {
        int ans=0,n=height.size();
        vector<int> st,rear(n,-1),front(n,-1),add(n);

        add[0]=height[0];

        for(int i=1;i<n;++i)
            add[i]=add[i-1]+height[i];

        for(int i=0;i<n;++i)
        {
            while(!st.empty()&&height[st.back()]<=height[i])
            {
                rear[st.back()]=i;
                st.pop_back();
            }
            st.push_back(i);
        }

        st.clear();

        for(int i=n-1;i>=0;--i)
        {
            while(!st.empty()&&height[st.back()]<height[i])
            {
                front[st.back()]=i;
                st.pop_back();
            }
            st.push_back(i);
        }

        for(int i=0;i<n;++i)
        {
            if(rear[i]==-1)break;

            ans+=(rear[i]-i-1)*height[i];

            ans-=(add[rear[i]-1]-add[i]);

            i=rear[i]-1;
        }

        for(int i=n-1;i>=0;--i)
        {
            if(front[i]==-1)break;

            ans+=(i-front[i]-1)*height[i];

            ans-=(add[i-1]-add[front[i]]);

            i=front[i]+1;
        }

        return ans;
    }
};

法二:从一个柱子的贡献度考虑,一个柱子贡献的积水量等于min(左边最大的柱子,右边最大的柱子)-本身高度。

从这个方法出发。

第一:双指针,从本身出发,然后更新左边最大值,从右边出发,更新右边最大值。时间复杂度:O(n^2) 略过不提。

第二:ST表或者线段树:既然是RMQ问题,那么可以考虑ST表或者线段树,实现O(logn)查询。这里选择的是线段树。时间复杂度:O(nlogn)


class Solution {
public:

    static const int maxn=2e4+10;
    int t[maxn<<2];

    void build(int x,int l,int r,vector<int> & vec)
    {
        if(l==r)
        {
            t[x]=vec[l];
            return;
        }

        int mid=l+r>>1;

        build(x<<1,l,mid,vec);
        build(x<<1|1,mid+1,r,vec);

        t[x]=max(t[x<<1],t[x<<1|1]);
    }

    int query(int x,int l,int r,int L,int R)
    {
        if(l>=L&&r<=R)return t[x];

        int mid=l+r>>1,ans=-1;

        if(mid>=L)ans=max(ans,query(x<<1,l,mid,L,R));
        if(mid<R)ans=max(ans,query(x<<1|1,mid+1,r,L,R));

        return ans;
    }

    int trap(vector<int>& height) {
        int n=height.size(),ans=0;
        build(1,0,n-1,height);

        for(int i=1,t;i<n-1;++i)
        {
            t=min(query(1,0,n-1,0,i-1),query(1,0,n-1,i+1,n-1))-height[i];
            if(t>0)ans+=t;
        }

        return ans;
    }
};

第三:我们发现这里的查询都是从端点出发的查询,很具有规律性,那么我们可以考虑存储对于每一个柱子来说左边的最大值和右边的最大值。即动态规划。时间复杂度:O(n)

class Solution {
public:
    int trap(vector<int>& height) {
        int n=height.size(),ans=0;
        vector<int> lmax(n,-1),rmax(n,-1);

        lmax.front()=height.front();
        rmax.back()=height.back();

        for(int i=1;i<n;++i)    
            lmax[i]=max(lmax[i-1],height[i]);

        for(int i=n-2;i>=0;--i)
            rmax[i]=max(rmax[i+1],height[i]);
        
        for(int i=1,t;i<n-1;++i)
        {
            t=min(lmax[i-1],rmax[i+1])-height[i];

            if(t>0)ans+=t;
        }

        return ans;
    }
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

DogDu

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

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

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

打赏作者

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

抵扣说明:

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

余额充值