给定n
个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。
上面是由数组 [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
分析
看到这个就会想到木桶效应,即所盛的水由短板决定。这里也是同理。
以上图为例分析一下过程,从左至右开始,位置1肯定是边界,也就是说位置1右边部分的水位绝对不可能高于此,所以我们可以从此开始遍历,如果后一位置的高度低于位置1,则假设这里的水位可以与1齐平,然后往后到位置6,一看,这里的高度高于位置1,也就是说,不管你6往后的洪水滔天,和位置1到5的水位无关,前面已经固定了。那么继续往后,由于不用考虑前面的,那么位置6相当于变为了位置1。但是到了位置8,此时最高为此位置,一直往后,到了位置12,由于位置12高度小于位置8,也就是说之前的假设的水位不可能真的达到,需要减去部分水位,那需要怎么减?这里处理就会麻烦,那么为什么1到6不用考虑减去呢?因为6高于1,假如12的位置也高于8,那么也不用考虑减去。所以根据短板效应,我们应该用短的那块高度做假设水位,即从高度低的向高度高的去移动。
但是如果从左至右依次遍历,除特殊情况,否则不可能始终从高度低的向高度高的走,所以不能从一端遍历,需要从两端同时遍历。
伪代码如下:
while(左端最高的位置在右端最高位置之前){
if(左端最高的高度<右端最高的高度){
左端位置向右移动,如果此位置小于最高则并记录下水位增加,否则此位置变为最高
}
else{
右端位置向左移动,如果此位置小于最高则记录下水位增加,否则次位置变为最高
}
}
Leetcode C++代码实现如下:
class Solution {
public:
int trap(vector<int>& height) {
int len = height.size();
if(len==0) return 0;
int ret=0;
int lindex = 0, rindex = len-1;
int left = height[lindex], right = height[rindex];
while(lindex<rindex){
if(left<right){
if(height[++lindex]<left){ret += left-height[lindex];}
else{left = height[lindex];}
}
else{
if(height[--rindex]<right){ret += right-height[rindex];}
else{right = height[rindex];}
}
}
return ret;
}
};
由于只遍历了一次,且存储空间和长度无关,则时间复杂度为O(n)
,空间复杂度为O(1)
,基本还是不错的。