前言
大家好,今天是@LetItbeSun 坚持每日两题的第19天。(😀
这个题目是学习labuladong的高频面试题,这个题目的形式不常见,所以我也没有什么思路,是看了题解之后理解的
题目
难度困难2139
给定 n 个非负整数表示每个宽度为 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 个单位的雨水(蓝色部分表示雨水)。
示例 2:
输入:height = [4,2,0,3,2,5] 输出:9
提示:
n == height.length
0 <= n <= 3 * 104
0 <= height[i] <= 105
思路
总结来说就是短板思想,就是对于每一个height[i]考虑它左边最高的板子和右边最高的板子,而height[i]能装的雨水=min(leftmax,rightmax)-height[i]
思路确定,接下来就是实现方法。
我们发现解决这个问题的关键就是求每个height[i]的leftmax和rightmax
方法一 暴力枚举
这个方法没什么说的就是对每个heght[i]都暴力枚举找到它的leftmax和rightmax 时间复杂度O(N^2)
方法二 备忘录
对于方法一的改进,方法二就是用空间换时间,建立leftmax和rightmax分别在O(N)时间内计算出它们的值。
int trap(vector<int>& height) {
//备忘录解法
int n=height.size();
//注意空集的时候返回
if(n==0){
return 0;
}
int ans=0;
vector<int> lmax(n,0),rmax(n,0); //lmax[i]表示h{0..i]中最高的 rmax[i]表示h[i...n-1]中最高的
lmax[0]=height[0];
rmax[n-1]=height[n-1];
for(int i=1;i<n-1;i++){
lmax[i]=max(height[i],lmax[i-1]);
}
for(int i=n-2;i>=1;i--){
rmax[i]=max(height[i],rmax[i+1]);
}
for(int i=1;i<n-1;i++){
ans+=min(rmax[i],lmax[i])-height[i];
}
return ans;
}
方法三 双指针
从两头开始用双指针left、right,对于这两个指针指到的left、right
此时的
l_max
是left
指针左边的最高柱子,但是r_max
并不一定是left
指针右边最高的柱子,这真的可以得到正确答案吗?其实这个问题要这么思考,我们只在乎
min(l_max, r_max)
。对于上图的情况,我们已经知道l_max < r_max
了,至于这个r_max
是不是右边最大的,不重要,重要的是height[i]
能够装的水只和l_max
有关。
l_max < r_max的情况,我们等于知道了右边的板子有比lmax还大的,根据短板决定,所以height[i]能够装的水就和l_max有关,
而l_max也正好就是left指针左边最高的柱子,同理right指针也一样。
int trap(vector<int>& height) {
//双指针法
int n=height.size();
if(n==0){
return 0;
}
int left=0,right=n-1;
int lmax=height[0],rmax=height[n-1];
int ans=0;
while (left<=right){
//更新lmax rmax
lmax=max(height[left],lmax);
rmax=max(height[right],rmax);
//左边的短板就累加left所在的height 右边的短板就累加right所在的height
if(lmax<rmax){ //右边至少有比lmax高的板
ans+=lmax-height[left];
left++;
}else{
ans+=rmax-height[right];
right--;
}
}
return ans;
}