当青训营遇上码上掘金
1.问题描述
现有 n 个宽度为 1 的柱子,给出 n 个非负整数依次表示柱子的高度,排列后如下图所示,此时均匀从上空向下撒青豆,计算按此排列的柱子能接住多少青豆。(不考虑边角堆积)
以下为上图例子的解析:
输入:height = [5,0,2,1,4,0,1,0,3]
输出:17
解析:上面是由数组 [5,0,2,1,4,0,1,0,3] 表示的柱子高度,在这种情况下,可以接 17 个单位的青豆。
2.问题分析
拿到这个题目有些迷惑,不太确定题干意思,分析后得知
- 每一根柱子相当于容器的一端,必须保证两边都存在柱子才能接住青豆
- 不考虑边角堆积意味着把青豆看做1*1的格子即可
- 当青豆的高度高于两端任意一端的柱子时,就会发生溢出
- 求接住多少青豆,就是求攒下的青豆面积
3.解题思路一
按列来分析,以当前列h为基准,若当前列左边最高柱子为l_max,右边最高柱子为r_max,min=Min(l_max, r_max)为两边柱子高度的最小值min,则有三种情况
- min<h,存不住青豆
- min=h,存不住青豆
- min>h,青豆=min-h
3.1代码和实现细节
完整代码实现:code.juejin.cn/pen/7199705…
public int getDou(int[] height){
int sum = 0, l_max, r_max, i, j, k;
//两边的列一定不能存青豆,故而不计算
for ( i = 1; i < height.length - 1; i ++){
l_max = 0;
r_max = 0;
//左边最高
for(j = i - 1; j >= 0; j--){
if(height[j] > l_max)
l_max = height[j];
}
//右边最高
for(k = i + 1; k < height.length; k++){
if(height[k] > r_max)
r_max = height[k];
}
//最高的最小值
int min = Math.min(l_max, r_max);
//只有最小值高于当前值才会存储青豆
if(min > height[i]){
sum += min - height[i];
}
}
return sum;
}
3.2复杂的分析
时间复杂度:O(n^2),遍历每一个列O(n),求每一个列两边的最高柱子O(n)
空间复杂度:O(1)
4.算法优化--空间换时间
根据上面的解题思路,遍历每一列都要计算一次左右的最高柱子,比较冗余,可以考虑动态规划,消耗一定的空间存储算出来的柱子最大值,即空间换时间
4.1优化思想
定义l_max (当前柱子左边的最高柱子值)和 r_max (当前柱子右边的最高柱子值)的值--计算时包括其自身。
定义两个指针left,right初始分别等于 0 和 n-1 ,left只向右边移动,right只向左边移动。
当l_h < r_h,计算left柱子所存青豆数l_max -l_h,left++,并维护l_max= Math.max(l_max, height[left]) 当l_h >= r_h,计算right柱子所存青豆数r_max -r_h,right++,并维护r_max= Math.max(r_max, height[right])
直到指针left,right相遇为止。
4.2代码和实现细节
public int getDou1(int[] height){
int sum = 0;
int left = 0, right = height.length-1;
int l_max = height[0], r_max = height[height.length - 1];
while(left < right){
int l_h = height[left];
int r_h = height[right];
if(l_h < r_h){
sum += l_max -l_h;
left++;
l_max = Math.max(l_max, height[left]);
}
else{
sum += r_max - r_h;
right --;
r_max = Math.max(r_max, height[right]);
}
}
return sum;
}
4.3复杂度分析
空间复杂度:O(n) 时间复杂度:O(n)
总结
该题思路与力扣上的接雨水类似,本题采用的动态规划方法用空间换时间,降低了时间复杂度,同时采用指针的思想,不使用额外数组,还是很有趣的。