题目
给定 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 * 10^4
- 0 <= height[i] <= 10^5
解法1:暴力(左右边界)
/**
* 思路:
* 核心思路:
* 找到每个坑,计算面积和
* 每个坑都有左右边界,我们要确定好边界
*
* 只要右边界没走到数组的末尾就一直循环
* 找左边界:记录左边界,只要下一个值比左边界小,就确定了左边界。否则,更新左边界为下一个值
* 之后向后找右边界:
* 记录最大的棒子
* 大于等于左边界就是右边界,否则一直向前找
* 如果找到数组末尾都没找到大于等于左边界的值,就把最大的棒子作为右边界
* 找到左右边界后计算坑的面积:min(height[l],hei[r])*(r-l-1)-夹在左右边界中的各个柱子的高度
*/
public int trap(int[] height) {
int sum=0,bar_sum=0,l=0,r = 0;
while (r<height.length-1) {
for (int i = l+1; i < height.length; i++) {
if (i==0||height[i] >= height[i - 1]) l = i;
else break;
}
int max_bar=l+1;
for (int i = l + 1; i < height.length; i++) {
//如果当前棒子大于max_bar就记录
max_bar=height[max_bar]>height[i]?max_bar:i;
if (height[i] >= height[l]) {
r = i;
break;
}
}
if (max_bar>=height.length)break;
//如果没有找到大于等于左棒子的,就把max_bar作为右棒子
if (r<=l) {
r=max_bar;
}
int area = Math.min(height[l], height[r]) * (r - l - 1);
for (int j = l + 1; j < r; j++) {
bar_sum += height[j];
}
sum += area - bar_sum;
l = r;
bar_sum = 0;
}
return sum;
}
时间复杂度:On^2
空间复杂度:O1
解法2:暴力(纵向注水)
/**
* 思路:
* 核心思路:给每一列的柱子注水,每个的柱子的最大注水量
*
* 头和尾的柱子无法注水,其他的柱子注水时,当前柱子能注多少水取决于,他左边最大的柱子和右边最大的柱子的高度。
* 如果当前柱子小于左右最大柱子时才可能注水
* 最大注水=min(height[l_max],height[r_max])-height[current]
*/
public int trap(int[] height) {
int sum=0;
for (int current=1;current<height.length-1;current++){
int l_max=current-1,r_max=current+1;
for (int l=current-1;l>=0;l--){
if (height[l]>height[l_max])l_max=l;
}
for (int r=current+1;r<height.length;r++){
if (height[r]>height[r_max])r_max=r;
}
int min_bar = Math.min(height[l_max], height[r_max]);
if (min_bar>height[current]) {
sum += min_bar - height[current];
}
}
return sum;
}
时间复杂度:On^2
空间复杂度:O1
解法3:左右夹逼
/**
* 思路:
* 从左到右,把每个柱子最多能注的水累加起来
*
* 记录左右柱子中最小的那个lower(地平线)
* 比较当前地平线lower是否高于level(水库高度)
* level-lower=当前柱子最大承载的水量
*/
public int trap(int[] height) {
int l=0,r=height.length-1,lower=0,level=0,water=0;
while (r>l){
lower=Math.min(height[l],height[r]);
level=Math.max(level,lower);
water+=level-lower;
}
return water;
}
时间复杂度:On
空间复杂度:O1
解法4:栈
/**
* 思路:
* 核心思路:
* 积水的面积 = 当前棒子和最低的那个棒子的高度差 * 他们的距离。
*
* 当前值小于栈顶元素就说明可能有积水产生,入栈
* 如果当前值大于栈顶元素,说明积水产生,出栈当前栈顶元素
* 比较栈顶和当前值的,找出小的那个min(积水产生的最大高度)。
* 求出栈顶和当前元素的距离 *(min-出栈的元素值)
*/
public int trap(int[] height) {
ArrayDeque<Integer> stack = new ArrayDeque<>();
int sum=0;
for (int current=0;current<height.length;current++){
while (!stack.isEmpty()&&height[current]>=height[stack.peek()]){
Integer pop = stack.pop();
if (stack.isEmpty())break;
int distance=current-stack.peek()-1;
int min=Math.min(height[stack.peek()],height[current]);
int area=(min-height[pop])*distance;
sum+=area;
}
stack.push(current);
}
return sum;
}
时间复杂度:On^2
空间复杂度:On