攒青豆 | 青训营 X 码上掘金

75 阅读2分钟

当青训营遇上码上掘金,非常高兴可以有机会参加青训营活动。这篇文章是针对 「青训营 X 码上掘金」主题创作活动中后端部分主题四创作的文章,主要内容包括题目描述,题目分析,算法复杂度分析还有代码实现。现在就让我们开始吧~

题目描述

现有 n 个宽度为 1 的柱子,给出 n 个非负整数依次表示柱子的高度,排列后如下图所示,此时均匀从上空向下撒青豆,计算按此排列的柱子能接住多少青豆。(不考虑边角堆积)

以下为上图例子的解析

输入:height = [5,0,2,1,4,0,1,0,3]

输出:17

解析:上面是由数组 [5,0,2,1,4,0,1,0,3] 表示的柱子高度,在这种情况下,可以接 17 个单位的青豆。

image.png

题目分析

这是一道非常典型的动态规划题目,我们用i来表示下标,n来表示height的长度。

创建两个长度为n的数组数组left和right,来分别表示柱子左边和右边能接住最多青豆的数量。

对于每个i,我们可以从前到后扫描(正向遍历)每个柱子相对于右侧能接住青豆最大的数量,用right[i]表示;从后到前扫描(反向遍历)每个柱子相对于左侧能接住青豆最大的数量,用left[i]表示。

每个i能接住的青豆数量等于min(left[i],right[i])−height[i]。遍历每个下标可以得到总共能接住的青豆数量。

算法复杂度分析

时间复杂度

总共遍历三次数组,时间复杂度为O(n)。

空间复杂度

需要创建两个长度为n的数组,空间复杂度为O(n)。

代码实现

java

class Solution {
    public int trap(int[] height) {
        int n = height.length, res = 0;
        int[] left = new int[n + 1];
        int[] right = new int[n + 1];

        for(int i = 0, preMax = height[0]; i < n; i++){
            preMax = Math.max(preMax, height[i]);
            right[i] = preMax;
        }

        for(int i = n-1, preMax = height[n-1]; i >= 0; i--){
            preMax = Math.max(preMax, height[i]);
            left[i] = preMax;
        }

        for(int i = 0; i < n; i++){
            res += Math.min(left[i], right[i]) - height[i];
            // System.out.println(i + " " + left[i] + " " + right[i] + " " + height[i]);
            // System.out.println(res);
        }

        return res;
    }
}