题目描述
https://leetcode-cn.com/problems/trapping-rain-water/
解法
解法一:超时无法AC(纵向遍历)
从底部向上,计算每个层的积水,层数为最大的数,每次要维护一个start和end
class Solution {
public int trap(int[] height) {
//一层一层的计算
//维护一个层数,和计算的雨水
if(height==null ||height.length<3) return 0;
int res = 0;
int max = 0;
for(int i=0;i<height.length;i++){
if(max<height[i]){
max = height[i];
}
}
for(int i=0;i<=max;i++){//层数,层数是最大值
res +=find(height,i);
}
return res;
}
/*
*返回第i层的积水数
*/
public int find(int[]height,int high){
int start=-1,end=-1;
//必须大于当前的层数的值,这之间的柱子才可能有积水
for(int i=0;i<height.length;i++){
if(height[i]>high){
start = i;
break;
}
}
for(int j=height.length-1;j>0;j--){
if(height[j]>high){//0我就要找比他大的
end = j;
break;
}
}
if((start!=-1 && end!=-1) && start<end){
//满足条件则有积水
int res = 0;
for(int i=start;i<end;i++){
if(height[i]<=high){//小于当前的层数,则
res++;
}
}
return res;
}
return 0;
}
}
解法二:单个格子的方法(水平遍历)
上面的算法我们是从纵向的维度去思考的,现在我们应该从水平的维度去看,计算每个格子可能存储的积水的高度,每次仅计算一个格子上的积水的单位
class Solution {
public int trap(int[] height) {
//一个格子一个格子的计算
if(height==null ||height.length<3) return 0;
int res = 0;
for(int i=1;i<height.length-1;i++){//从1开始才有积水
int maxLeft = 0,maxRight = 0;
for(int j=i-1;j>=0;j--){
maxLeft = Math.max(maxLeft,height[j]);
}
for(int j=i+1;j<height.length;j++){
maxRight = Math.max(maxRight,height[j]);
}
int min = Math.min(maxRight,maxLeft);
if(min - height[i]<=0)//每个格子能积多少水
continue;//不能积水
else res += min-height[i];
}
return res;
}
}
时间复杂度达到了O(n*n),空间复杂度O(1),注意在查找左右两边上耗费了时间,有些时候根本不需要更新该高度,所以我们可以提前存储这些值
解法三:动态编程
使用一个rightMax数组来存储i往右的最大高度柱子,使用一个leftMax数组来存储i往左边的最大高度柱子。
class Solution {
public int trap(int[] height) {
//一个格子一个格子的计算其上的积水单位
if(height==null ||height.length<3) return 0;
int res = 0;
int[]leftMax = new int[height.length];
int[]rightMax = new int[height.length];
leftMax[0] = height[0];
rightMax[height.length-1] = height[height.length-1];
//使用一个rightMax来存储i往右的最大高度柱子 使用一个leftMax来存储i往左边的最大高度柱子
for(int i=1,j=height.length-2;i<height.length && j>=0;i++,j--){
leftMax[i] = Math.max(height[i],leftMax[i-1]);
rightMax[j] = Math.max(height[j],rightMax[j+1]);
}
for(int i=1;i<height.length;i++){
int min = Math.min(leftMax[i],rightMax[i]);
if(min - height[i]<=0)//每个格子能积多少水
continue;//不能积水
else res += min-height[i];
}
return res;
}
}
解法四:双指针法
我们使用双指针,方法二里面,大的哪个值决定了我们的遍历方向
- 比如右边的值大,我们则计算左边的柱子上的积水单位即可。
因为左边的小,我们知道左边更有可能存在积水单位
右边指针不动,左边指针动,计算完一个柱子 - 反之,左边指针不动,右边指针动
class Solution {
public int trap(int[] height) {
//一个格子一个格子的计算其上的积水单位
if(height==null ||height.length<3) return 0;
int res = 0;
int left = 0;int right = height.length-1;
int leftMax = height[0],rightMax=height[right];
//双指针
while(left<right){
//判断哪个大
if(height[left]<height[right]){
//我们则计算左边的柱子的积水
if(height[left]>leftMax ){//没有积水,当前的left柱子上
leftMax=height[left];
}else//有积水
res+=leftMax-height[left];
//左边指针移动一位
left++;
}else{
//我们则计算右边的柱子的积水
if(height[right]>rightMax ){//没有积水,当前的right柱子上
rightMax=height[right];
}else//有积水
res+=rightMax-height[right];
//右边指针移动一位
right--;
}
}
return res;
}
}