当青训营遇上码上掘金
题目
现有 n 个宽度为 1 的柱子,给出 n 个非负整数依次表示柱子的高度,排列后如下图所示,此时均匀从上空向下撒青豆,计算按此排列的柱子能接住多少青豆。(不考虑边角堆积)
样例
输入:height = [5,0,2,1,4,0,1,0,3]
输出:17
解析:上面是由数组 [5,0,2,1,4,0,1,0,3] 表示的柱子高度,在这种情况下,可以接 17 个单位的青豆。
分析
这是一道非常经典的算法题(我在高一的时候就见过了。。。),可以在 Leetcode 上面找到题目的原型。
思路一
如果我们以行为单位看,那么如果一行中的某一列的高度低于行高,且其左右均存在高于或等于行高的列,那么这个这个格子将会攒一个单位的青豆,否则不会攒青豆。
遍历每一行每一列,可以找到全部可以攒青豆的格子。
思路二
如果我们以列为单位看,那么如果某一列的左侧和右侧均存在高于这一列的行高的列,那么这一列会攒 min(maxLeft, maxRight) - height单位个青豆。
比如样例中的第二列的行高(0),低于左侧最大值(5)且低于右侧最大值(4),那么它会积攒 4 个青豆。
遍历每一列,可以得到接住青豆的总数
实现
我最后选择了思路二(我认为更好实现)
源码
public class Solution {
public int trap(int[] height) {
int sum = 0;
for (int i = 1; i < height.length - 1; i++) {
int left = 0;
int right = 0;
for (int j = i + 1; j <= height.length - 1; j++) {
if (height[j] > right) {
right = height[j];
}
}
for (int j = i - 1; j >= 0; j--) {
if (height[j] > left) {
left = height[j];
}
}
int min = Math.min(left, right);
sum += min > height[i] ? min - height[i] : 0;
}
return sum;
}
}
复杂度
- 时间复杂度:
- 空间复杂度:
优化
很显然这里还有很大的优化空间,因为每一次判断一列攒青豆的数量时,都要重新判断这一列左右最高的墙,非常浪费时间。
而且,同样显然的是,如果某一列右侧的最高的列不是其右侧临近的一列时,其右侧临近的一列的右侧的最高的列,应该与之相同。
所以我们可以使用动态规划,先分别算出maxLeft[i] = Math.max(maxLeft[i-1], height[i]) 和 maxRight[],再for遍历两个新数组。
复杂度
- 时间复杂度:
- 空间复杂度:
再优化
我们构造的数组中大部分数据是重复的(局部最大值),这意味着我们可以使用两个数分别代替局部数组,以减少空间复杂度。
可以使用双指针实现,篇幅有限,这里就不演示了。
复杂度
- 时间复杂度:
- 空间复杂度: