题目详情
给定 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
三种解法
第一种解法是我自己的想法,一开始比较暴力的方法,和栈其实有点像。
是分高度层来计算area的。
public int trap(int[] height) {
//思路有了,这题和11题一点都不一样,需要考虑的东西实际很复杂
//也是双指针,但需要记录下当前的高度,每次高度来计算一下面积
//当后面的高度大于前面的,只需要记录下当前由于高度增加而新增的面积,从而防止重复计算
int l=0, r=height.length-1, res=0;
int cur_hi = 0;//当前高度
while(l<r){
//只要当高度更新了,才会计算面积
if(Math.min(height[l],height[r])>cur_hi){
res += calcArea(height,cur_hi,l,r);
cur_hi = Math.min(height[l],height[r]);
}
//移动l与r
if(height[l]<=height[r]){
l++;
}
else{
r--;
}
}
return res;
}
/**
计算中间能接多少雨水
**/
private int calcArea(int[] height,int cur_hi, int l, int r){
if(r-l<2){
return 0;
}
int real_height = Math.min(height[l],height[r]);
int area = 0;
for(int i=l+1; i<r; i++){
//每个节点的高度如果低于cur_hi,就按cur_hi来看,如果高于cur_hi,就按自己看
int cur = height[i]>cur_hi ? height[i] : cur_hi;
//每次按照边界和高度确定当前的接水高度
area += Math.max(0,real_height-cur);
}
return area;
}
该解法充分利用了剪枝,不过因为复杂度到O(n2)了,所以有些慢。4ms
然后是双指针O(n)的解法。
public int trap(int[] height) {
// 来进行双指针O(n)的练习
// 这里的逻辑是双指针进行走,每次记录左右已遍历最大值
// 如果移动到当前值小于自己这部分最大值,则意味着两件事。
// 第一件事便是,因为当前值移动,所以另一部分一定大于当前值。因为双指针逻辑是小的移动
// 第二件事便是,因为当前值小自己这部分最大值,所以在自己位置一定能装下 这部分最大值-自己值的水
// 这里这部分最大值<另一边显然,不然,另一边就会移动
// 所以秉承着这样的想法,整个双指针法就得到了证明,下面来实现代码逻辑
int l=0, r=height.length-1, lmax=0, rmax=0, res=0;
while(l<r){
if(height[l]<=height[r]){
if(height[l]>lmax){
lmax = height[l];
}
else{
res += lmax-height[l];
}
l++;
}
else{
if(height[r]>rmax){
rmax = height[r];
}
else{
res += rmax-height[r];
}
r--;
}
}
return res;
}
具体的思路以及理解证明写在注释里了。
下一种是栈。
public int trap(int[] height) {
//果然就很快
//下面尝试用栈的方式找
//栈的思路在于从左开始往右走
//如果栈空,入栈
//如果小于栈顶,入栈(其实就是小于前一个数)
//如果大于,则一直与栈里的元素比较,计算相互匹配上的area,直到当前柱子比栈顶柱子低(其实就是与左边柱子计算中间能放多少雨水)
int ans = 0, current = 0;
Deque<Integer> stack = new LinkedList<Integer>();
while (current < height.length) {
while (!stack.isEmpty() && height[current] > height[stack.peek()]) {
//这里只要栈不为空,cur是不变的,因为右边高的柱子要一直和左边低的每个柱子进行匹配
int top = stack.pop();
if (stack.isEmpty())
break;
int distance = current - stack.peek() - 1;
int bounded_height = Math.min(height[current], height[stack.peek()]) - height[top];
ans += distance * bounded_height;
}
stack.push(current++);
}
return ans;
}