力扣---接雨水---单调队列+动态规划+双指针

题目:

单调队列思想:

没有思路的小伙伴可以先把这个想清楚哦:力扣hot10---大根堆+双端队列-CSDN博客

从上面的图就可以发现,如果柱子呈递减序列,那么不会接到雨水,只要有一个小凸起柱子,那么这个柱子就会和之前的柱子接到雨水。所以我们维护一个递减序列,如果遍历到某个柱子接到雨水时,就把前面比他矮的柱子pop掉,同时因为接到了雨水,前面的柱子高度也会发生变化,变成了接完雨水后的最高值(height数组中的元素值需改变的原因为:考虑到后面还会有更高的柱子使得该柱子再次接到点雨水,我们需要改变柱子的长度)。一个柱子能接雨水的最大值该怎么求呢?首先,我们比较遍历到的这个柱子和队列中第一高的柱子哪个更低,如果该值为lower,那么接到的雨水部分英文lower-height[ i ]。不清晰的友友直接看代码吧~

代码:

C++:

class Solution {
public:

    int calculate(vector<int>& height,int idx_r,int idx_l){
        int s=0;
        int min_=min(height[idx_l],height[idx_r]);
        int r=height[idx_r];
        for(int i=idx_l;i<idx_r;i++){
            if(r>height[i]){
                s+=min_-height[i];
                height[i]=min_;
            }
        }
        return s;
    }
    int trap(vector<int>& height) {
        //单调队列(维护递减的序列)
        deque<pair<int,int>> q;
        int len=height.size();
        int s=0;
        for(int i=0;i<len;i++){
            //出
            while(!q.empty() and q.back().first<=height[i]){
                s+=calculate(height,i,q.front().second); //计算新加的面积大小,同时改变数组中的元素,因为已经接了雨水
                q.pop_back();
            }
            //进
            q.push_back({height[i],i});
        }
        return s;
    }
};

Python:

class Solution:
    def calculate(self,height:List[int],idx_r:int,idx_l:int) -> int:
        s=0
        min_=min(height[idx_l],height[idx_r])
        r=height[idx_r]
        for i in range(idx_l,idx_r):
            if r>height[i]:
                s+=min_-height[i]
                height[i]=min_
        return s
    def trap(self, height: List[int]) -> int:
        q=deque()
        height_len=len(height)
        s=0
        for i in range(height_len):
            while q and q[-1][0]<=height[i]:
                s+=self.calculate(height,i,q[0][1])
                q.pop()
            q.append((height[i],i))
        return s

动态规划思想:

题解说是直觉(然后用动态规划来优化),我是真没有这直觉呀(...)

初始想法写到代码里面啦,这里再贴一遍:

初始想法:对于每个height[i],所接的雨水量等于该柱子左面的所有柱子高度最大值,以及
该柱子右面的所有柱子高度最大值,这两个值取较小的那一个,并减去该柱子原有的高度。

代码如下(直觉版):

class Solution {
public:
    int trap(vector<int>& height) {
        //初始想法:对于每个height[i],所接的雨水量等于该柱子左面的所有柱子高度最大值,以及
        //该柱子右面的所有柱子高度最大值,这两个值取较小的那一个,并减去该柱子原有的高度
        int len=height.size();
        int res=0;
        for(int i=0;i<len;i++){
            int maxl=0;
            int maxr=0;
            for(int j=0;j<i;j++){
                maxl=max(maxl,height[j]);
            }
            for(int j=i+1;j<len;j++){
                maxr=max(maxr,height[j]);
            }
            int temp=min(maxl,maxr)-height[i];
            if(temp>0){
                res+=temp;
            }
        }
        return res;
    }
};

但是!!!有一个点是过不去的,那么我们继续用动态规划思想来优化。

 既然代码中有这样一个过程,是不是感觉有很多地方做了无用功?为什么遍历每一个height[i]时都要计算左右的最大值呢,不能一次性记录嘛?(感觉有点前缀和思想)

for(int i=0;i<len;i++){
    int maxl=0;
    int maxr=0;
    for(int j=0;j<i;j++){
        maxl=max(maxl,height[j]);
    }
    for(int j=i+1;j<len;j++){
        maxr=max(maxr,height[j]);
    }
}

代码如下(动态规划版):

C++:

class Solution {
public:
    int trap(vector<int>& height) {
        //初始想法:对于每个height[i],所接的雨水量等于该柱子左面的所有柱子高度最大值,以及
        //该柱子右面的所有柱子高度最大值,这两个值取较小的那一个,并减去该柱子原有的高度
        int len=height.size();
        int res=0;
        vector<int> maxl(len,0);
        vector<int> maxr(len,0);
        for(int i=1;i<len;i++){
            maxl[i]=max(maxl[i-1],height[i-1]);
        }
        for(int i=len-2;i>=0;i--){
            maxr[i]=max(maxr[i+1],height[i+1]);
        }
        for(int i=0;i<len;i++){
            int temp=min(maxl[i],maxr[i])-height[i];
            if(temp>0){
                res+=temp;
            }
        }
        return res;
    }
};

Python:

class Solution:
    def trap(self, height: List[int]) -> int:
        len_height=len(height)
        res=0
        maxl=[0]*len_height
        maxr=[0]*len_height
        for i in range(1,len_height):
            maxl[i]=max(maxl[i-1],height[i-1])
        for i in range(len_height-2,-1,-1):
            maxr[i]=max(maxr[i+1],height[i+1])
        for i in range(len_height):
            temp=min(maxl[i],maxr[i])-height[i]
            if temp>0:
                res+=temp
        return res

双指针思路:

感觉题解说的有点迷(没太看懂,,)

先借用一下题解中的图:

首先我们先来看第一张图,由 i 和 j 两个指针,因为height[ j ]>height[ i ],所以 i 和 i 的右侧(直到 j )都可以接到雨水,右侧图片同理。有没有一点眼熟,把两个指针放到两侧,那个高度小于对方高度了,就像中间移动。有点像这道题:力扣---接雨水---单调队列-CSDN博客

我们还是看左面这张图,我们将指针 i 左侧的最高柱子高度记为MaxLeft,将指针 j 右侧的最高柱子高度记为MaxRight,我们就可以得到柱子 i 接到的雨水=MaxLeft-height[ i ]。???为什么就确定柱子 i 左侧的最大值一定小于右侧的最大值呢???右侧已经出现了一个较大的柱子 j 了,如果height[ i ]>height[ j ],那么说明 j 柱子是可以接到雨水的,那么 i 指针也不会向右移动,反而是 j 指针向左移动,所以如果柱子 i 左侧的最大值大于右侧的最大值的话,我们应该计算的是柱子 j 接到的雨水了(=MaxRight-height[ j ],因为左侧柱子已经足够高)!很绕很绕很绕。。。

代码如下:

class Solution {
public:
    int trap(vector<int>& height) {
        //初始想法:对于每个height[i],所接的雨水量等于该柱子左面的所有柱子高度最大值,以及
        //该柱子右面的所有柱子高度最大值,这两个值取较小的那一个,并减去该柱子原有的高度
        int len=height.size();
        int res=0;
        int MaxLeft=0;
        int MaxRight=0;
        int left=0;
        int right=len-1;
        while(left<right){
            MaxLeft=max(MaxLeft,height[left]);
            MaxRight=max(MaxRight,height[right]);
            if(height[left]<height[right]){
                res+=MaxLeft-height[left];
                left++;
            }
            else{
                res+=MaxRight-height[right];
                right--;
            }
        }
        return res;
    }
};

Python代码和C++类似,就不附啦~

  • 5
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值