当青训营遇上码上掘金
我选的主题是攒青豆
主题 4:攒青豆
现有 n 个宽度为 1 的柱子,给出 n 个非负整数依次表示柱子的高度,排列后如下图所示,此时均匀从上空向下撒青豆,计算按此排列的柱子能接住多少青豆。(不考虑边角堆积)
题目分析
本题虽然换了个元素,但是和力扣的42题:接雨水是完全相同的,所以可以根据做力扣题时的思路来解决这道题目。
解题思路
思路1:暴力求解
审题之后可以发现每个柱子顶部可以储水的高度,当前柱子的左右两侧最大高度的较小者减去当前柱子的高度。
因此我们只需要遍历每个柱子,累加每个柱子可以储水的高度即可。
public int trap(int[] height) {
if (height == null || height.length == 0) return 0;
int res = 0;
for (int i = 1; i < height.length - 1; i++) {
int leftMax = 0, rightMax = 0;
for (int j = 0; j <= i; j++) {
leftMax = Math.max(leftMax, height[j]);
}
for (int j = i; j < height.length; j++) {
rightMax = Math.max(rightMax, height[j]);
}
res += Math.min(leftMax, rightMax) - height[i];
}
return res;
}
思路2:动态规划
我们注意到,思路一中,对于每一列,我们求它左边最高的墙和右边最高的墙,都是重新遍历一遍所有高度,这里我们可以优化一下。
首先用两个数组,max_left [i] 代表第 i 列左边最高的墙的高度,max_right[i] 代表第 i 列右边最高的墙的高度。
对于 max_left我们其实可以这样求,max_left [i] = Max(max_left [i-1],height[i-1])。它前边的墙的左边的最高高度和它前边的墙的高度选一个较大的,就是当前列左边最高的墙了。
对于 max_right我们可以这样求,max_right[i] = Max(max_right[i+1],height[i+1]) 。它后边的墙的右边的最高高度和它后边的墙的高度选一个较大的,就是当前列右边最高的墙了。
这样,我们再利用思路一的算法,就不用在 for 循环里每次重新遍历一次求 max_left 和 max_right 了。
public int trap(int[] height) {
int sum = 0;
int[] max_left = new int[height.length];
int[] max_right = new int[height.length];
for (int i = 1; i < height.length - 1; i++) {
max_left[i] = Math.max(max_left[i - 1], height[i - 1]);
}
for (int i = height.length - 2; i >= 0; i--) {
max_right[i] = Math.max(max_right[i + 1], height[i + 1]);
}
for (int i = 1; i < height.length - 1; i++) {
int min = Math.min(max_left[i], max_right[i]);
if (min > height[i]) {
sum = sum + (min - height[i]);
}
}
return sum;
}