题目:
单调队列思想:
没有思路的小伙伴可以先把这个想清楚哦:力扣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=0min_=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 sdef trap(self, height: List[int]) -> int:q=deque()height_len=len(height)s=0for 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=0maxl=[0]*len_heightmaxr=[0]*len_heightfor 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+=tempreturn 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++类似,就不附啦~