题目
给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。
上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图,在这种情况下,可以接 6 个单位的雨水(蓝色部分表示雨水)。 感谢 Marcos 贡献此图。
示例:
输入: [0,1,0,2,1,0,1,3,2,1,2,1]
输出: 6
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/trapping-rain-water
题解
思路1
- 按列求,对于每一列,若Math.min(左边最高值,右边最高值)>当前列高度,则高度差等于当前列接的雨水。
- 用DP优化,提前求出lMax、rMax数组,然后DP。时间复杂度O(n),空间复杂度O(n)
思路2(较优,直接看这个)
- 使用双指针,可以只遍历一次,因为当前列是由“Math.min(左边最高值,右边最高值)”决定的,所以若能确定哪边的最高值更小,则有哪边的最高值就可以了,不需要两边的最高值都知道。
- 由此,“我们可以认为如果一端有更高的条形块(例如右端),积水的高度依赖于当前方向的高度(从左到右)。当我们发现另一侧(右侧)的条形块高度不是最高的,我们则开始从相反的方向遍历(从右到左)。”
- lMax,rMax由变量维护,分别表示当前左指针左边的最大值,右指针右边的最大值。代替了数组。故空间复杂度优化至O(1)。
思路1代码(时间复杂度O(n),空间复杂度O(n))
class Solution { public int trap(int[] height) { if(height==null||height.length==0){ return 0; } int len=height.length; int[] lMax=new int[len]; int[] rMax=new int[len]; lMax[0]=0; rMax[len-1]=0; for(int i=1;i<len;++i){ lMax[i]=Math.max(lMax[i-1],height[i-1]); } for(int i=len-2;i>=0;--i){ rMax[i]=Math.max(rMax[i+1],height[i+1]); } int ans=0; for(int i=0;i<len;++i){ int dif=Math.min(lMax[i],rMax[i])-height[i]; ans=dif>0?ans+dif:ans; } return ans; } }
思路2代码(较优,时间复杂度O(n),空间复杂度O(1))
class Solution { public int trap(int[] height) { if(height==null||height.length==0){ return 0; } int len=height.length; int lMax=0; int rMax=0; int l=0; int r=len-1; int ans=0; while(l<r){ if(height[l]<=height[r]){ if(height[l]>=lMax){ lMax=height[l]; } else{ ans+=lMax-height[l]; } ++l; } else{ if(height[r]>=rMax){ rMax=height[r]; } else{ ans+=rMax-height[r]; } --r; } } return ans; } }