42. 接雨水

#42. 接雨水

2020/4/4每日一题打卡√(通过的第一道困难题)
参考题解:单调栈O(n) --sweetiee
接雨水问题的超完全手册

题目描述
在这里插入图片描述
解题思路
1、万能暴力法
思路也很清楚,按列求出该列能接住的雨水,然后再累加。
求每一列雨水的方法:该列能接的雨水受两边最大的高度影响,等于两边最大高度的最小值减去本身值。

public int trap(int[] height) {        
	int re = 0,left = 0,right = 0;        
	for(int i = 0;i < height.length;i++){            
		int p = i-1,q = i+1;            
		left = right = height[i];
		//分别找出左边和右边的最大值            
		while(p >= 0 || q < height.length){               
			if(p >= 0 && height[p] > left)              
				left = height[p];                
			if(q < height.length && height[q] > right)  
				right = height[q];                
			p--;q++;            
		}            
		int cur = Math.min(left,right) - height[i];        
		re += cur ;        
	}        
	return re;    
}

提交结果:(O(n2)果然很慢)
在这里插入图片描述
2、动态编程
在暴力法中,对每个元素都要进行向左向右最大值的搜索,这个过程其实有很多重复比较,可以利用动态规划的思想,从第一个元素开始,保存前一个元素两边的最大值,下一个元素只要与前一个元素的最大值比较就能得到新的最大值。
在这里插入图片描述

public int trap(int[] height) {        
	int re = 0,n = height.length;        
	int left[] = new int[n],right[] = new int[n];        
	if(n == 0)            
		return 0;        
	left[0] = height[0];right[n-1] = height[n-1];        
	for(int i = 1;i < n;i++)//找出当前元素左边的最大值       
		left[i] = Math.max(height[i], left[i-1]);     
	for(int i = n - 2;i >= 0;i--)//找出当前元素右边的最大值 
		right[i] = Math.max(height[i],right[i+1]);    
	for(int i = 0;i < n;i++)            
		re += Math.min(left[i],right[i]) - height[i];     
	return re;    
}

提交结果:
在这里插入图片描述
复杂度分析:
时间复杂度O(n),从前往后遍历数组
空间复杂度O(n),需要额外的数组空间来保存最大值
3、单调栈
这道题其实真正考察的是单调栈解题的思想。
单调栈顾名思义就是栈里面的元素按照递增或者递减的顺序排列
这个有点难懂,我看别人的题解才明白,所以代码里写了很多注释,希望以后还能记得为什么这么写

public int trap(int[] height) {        
	if(height == null)            
		return 0;        
	int re = 0;        
	Stack<Integer> stack = new Stack<>();        
	for (int i = 0; i < height.length; i++) {            
	//当栈非空,而且当前元素大于栈顶元素则出栈(栈里面存放的是下标
	while(!stack.isEmpty() && height[i] > height[stack.peek()]){
		int h = height[stack.pop()]; 
		//h保存出栈的栈顶元素值,新栈顶此时是左边界                		//如果栈内元素一直相同就一直出栈,这个也可以省略不写                
		while(!stack.isEmpty() && height[stack.peek()] == h)
			stack.pop();                
		if(!stack.isEmpty())                 
		//如果出栈后栈还不为空,Math.min(height[stack.peek()], height[i])求的是左右边界最小的那个,                
		//-h则是求能放下多少水量,还要乘以长度                  
		re += (Math.min(height[stack.peek()], height[i]) - h)*(i - stack.peek() - 1);            
	}            
		stack.push(i);  //如果是按照顺序则入栈
	}        
	return re;
    }

提交结果:
在这里插入图片描述
复杂度分析:
按道理每个元素最多入栈一次,时间复杂度应该是O(n)的,但是这种方法实际提交时间表现却不太好,可能是因为出入栈操作有点费时间?
4、双指针法
在这里插入图片描述
在这里插入图片描述

public int trap(int[] height) {      
	int re = 0,i = 0,j = height.length - 1,leftmax = 0,rightmax = 0;  //左边最大值和右边最大值        
	while(i <= j) {            
		if(leftmax < rightmax) {  //如果左边最大值小于右边最大值                
			leftmax = Math.max(leftmax, height[i]);  //向右递推求左边最大值                
			re += leftmax - height[i++];            
		}           
		else {                
			rightmax = Math.max(rightmax, height[j]);                
			re += rightmax - height[j--];            
		}           
	}                    
	return re;    
}

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值