当青训营遇上码上掘金 | 攒青豆

49 阅读1分钟

当青训营遇上码上掘金

我选的主题是攒青豆

主题 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;
 }
 ​