题目:
单调队列思想:
没有思路的小伙伴可以先把这个想清楚哦:力扣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++类似,就不附啦~