42. 接雨水
链接
题目描述
1.按行接
O
(
M
∗
N
)
O(M*N)
O(M∗N) 【不用看】
从第一行遍历到最后一行,统计雨水的数量
对于第 i 行:如果当前高度 >= i ,res += temp , temp = 0
如果当前高度 < i, temp ++
class Solution {
public int trap(int[] height) {
if(height == null || height.length <= 2){
return 0;
}
int maxNum = getMax(height);
int res = 0;
for(int i = 1 ; i <= maxNum ; i++){//从第一层开始加到最高层
int temp = 0;//记录每一层一共有多少雨水
boolean isStart = false;
for(int j = 0; j < height.length ;j++ ){//从数组中每个位置开始遍历
if(isStart && height[j] < i){
temp++;
}
if(height[j] >= i){
res += temp;
temp = 0;
isStart = true;
}
}
}
return res;
}
private int getMax(int[] height){
int max = height[0];
for(int i = 0; i < height.length;i++){
max = Math.max(max,height[i]);
}
return max;
}
}
时间复杂度:
O
(
m
∗
n
)
O(m*n)
O(m∗n), 最大的数是 m, 长度为 n
空间复杂度:
O
(
1
)
O(1)
O(1)
leetcode 超时
2. 按列接 O ( N 2 ) O(N^2) O(N2)
对于每一列来说,能装的雨水是左边最高的墙和右边最高的墙的最小值-当前列的高度(墙的高度要 >= 当前列的高度,不然就接不了雨水了)
class Solution {
public int trap(int[] height) {
if(height == null || height.length <= 2){
return 0;
}
int res = 0;
for(int i = 1; i < height.length-1;i++){//对于每一列,要找出左边最高的墙,和右边最高的墙
//找到左边最高的墙
int leftTop = height[0];
for(int m = 0; m < i; m++){
leftTop = Math.max(leftTop,height[m]);
}
//找到右边最高的墙
int rightTop = height[height.length-1];
for(int n = height.length-1 ; n > i; n-- ){
rightTop = Math.max(rightTop,height[n]);
}
//判断一下左边最高的墙和右边最高的墙是否比当前列的高度大
if(leftTop >= height[i] && rightTop >= height[i]){
//当前列能装的雨水
int cur = Math.min(leftTop,rightTop)-height[i];
res += cur;
}
}
return res;
}
}
时间复杂度:
O
(
N
2
)
O(N^2)
O(N2)
空间复杂度:
O
(
1
)
O(1)
O(1)
3.动态规划 时间: O ( n ) O(n) O(n) 空间: O ( n ) O(n) O(n)
对于第二问解法的优化
我们注意到,对于每一列,都求左边最高的墙和右边最高的墙,都是重新遍历一次高度
用两个数组:
max_left[i]
: 代表左边最高的墙的高度(不包括自己)
max_right[i]
: 代表右边最高的墙的高度(不包括自己)
则 max_left[i] = max(max_left[i-1],height[i])
max_right[i] = max(max_right[i+1],height[i])
class Solution {
public int trap(int[] height) {
if(height == null || height.length <= 2){
return 0;
}
int res = 0;
//生成左边最高墙
int[] max_left = new int[height.length];
for(int i = 1; i < height.length-1 ;i++){
max_left[i] = Math.max(max_left[i-1] , height[i-1]);
}
//生成右边最高墙
int[] max_right = new int[height.length];
for(int i = height.length-2 ; i > 0 ;i--){
max_right[i] = Math.max(max_right[i+1] , height[i+1]);
}
//对每个列,计算结果
for(int i = 1; i < height.length-1 ;i++){
int cur = Math.min(max_left[i],max_right[i])-height[i] > 0 ? Math.min(max_left[i],max_right[i])-height[i] : 0 ;
res += cur;
}
return res;
}
}
时间复杂度: O ( n ) O(n) O(n)
空间复杂度: O ( n ) O(n) O(n),用来保存每一列左边最高的墙和右边最高的墙。
4.双指针
动态规划中,对空间复杂度进行进一步的优化
由于:
max_left[i] = max(max_left[i-1],height[i])
max_right[i] = max(max_right[i+1],height[i])
我们发现可以用一个变量来表示左边最高的高度,再用一个变量表示右边最高的高度
class Solution {
public int trap(int[] height) {
if(height == null || height.length <= 2){
return 0;
}
int res = 0;
//左右指针
int left = 1;
int right = height.length-2;
int left_max = height[0];//左指针左边最高的柱子
int right_max = height[height.length-1];//右指针右边最高的柱子
//每次计算的是 left 指针 或者 right 指针指向的柱子的雨水
while(left <= right){
//如果 left指针 左边最高 < right指针 右边最高,根据木桶原理,我们现在计算 left指针 的雨水
if(left_max < right_max){
int cur = left_max - height[left] > 0 ? left_max - height[left] : 0;
left_max = Math.max(left_max,height[left]);
res += cur;
left++;
}else{
int cur = right_max - height[right] > 0 ? right_max - height[right] : 0;
right_max = Math.max(right_max,height[right]);
res += cur;
right--;
}
}
return res;
}
}
时间复杂度: O ( n ) O(n) O(n)
空间复杂度: O ( 1 ) O(1) O(1)