当青训营遇上码上掘金
攒青豆
- 现有 n 个宽度为 1 的柱子,给出 n 个非负整数依次表示柱子的高度,排列后如下图所示,此时均匀从上空向下撒青豆,计算按此排列的柱子能接住多少青豆。(不考虑边角堆积)
以下为上图例子的解析:
输入:height = [5,0,2,1,4,0,1,0,3]
输出:17
解析:上面是由数组 [5,0,2,1,4,0,1,0,3] 表示的柱子高度,在这种情况下,可以接 17 个单位的青豆。
思路
思路1:前缀后缀最大值
经过观察后可以发现
- 每个位置的青豆数量 = min(左边最高的柱子的高度, 左边最高的柱子的高度) - 该位置的高度 为什么呢?因为槽子的形成一定是由左边和右边最高构成的,所以该位置的豆子数就是由两端最短的柱子和当前的高度所决定。
- 所以我们可以使用两个数组premx和aftmx分别表示前缀最大值和后缀最大值
- 最后再遍历一遍数组,计算每个位置的个数,计入总和即可得到答案
main函数代码见:码上掘金code.juejin.cn/pen/7196666…
int beans(vector<int>& height) {
int ans = 0, n = height.size();
vector<int> premx(n, height.front()), aftmx(n, height.back());
for(int i = 1; i < n; i++) premx[i] = max(premx[i-1], height[i]);
for(int i = n-2; i >= 0; i--) aftmx[i] = max(aftmx[i+1], height[i]);
for(int i = 0; i < n; i++){
ans += min(premx[i], aftmx[i]) - height[i];
}
return ans;
}
思路2: 双指针
上面这个方法的时间复杂度已经达到O(n)了,但是空间复杂度却还是有点大的,还可以怎样进行优化呢?
考虑一下,如果我们已经知道了一部分前缀的最大值和一部分后缀的最大值,哪些位置是可以计算的?
- 如果当前位置l的前缀最大值为l_max, 根据思路一来计算,他就等于
前后缀最大值中的最小值减去当前高度 - 此时前缀最大值我们已经知道,那么后缀最大值呢?
- 考虑后缀r处后缀大值为r_max
- 如果 发现 l_max < r_max
- 因为r_max 一定会越来越大,所以l处的min(前缀最大值,后缀最大值)一定等于l_max是吧
- 所以l处的豆子数量就是 l_max - height[l]
- 然后我们移动l指针往后
- 同理如果:l_max >= r_max
- r处的豆子数量就是 r_max - height[r],移动r指针往前
- main函数代码见:码上掘金code.juejin.cn/pen/7198098…
int beans(vector<int>& height) {
int n = height.size(), ans = 0, l = 0, r = n - 1, l_max = 0, r_max = 0;
while(l <= r){
l_max = max(l_max, height[l]);
r_max = max(r_max, height[r]);
if(l_max < r_max){
ans += l_max - height[l];
l++;
}else{
ans += r_max - height[r];
r--;
}
}
return ans;
}