攒青豆

67 阅读1分钟

当青训营遇上码上掘金

题目描述

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

image.png

输入

[5,0,2,1,4,0,1,0,3]

输出

17

解题思路

第一眼看到,这不是大名鼎鼎的接雨水嘛,leetcode 42 题,就是更改了文案,这里我们就不再赘述了,直接上代码写思路。

版本一 暴力即是美学

首先我们来看一下暴力解法,我们可以遍历每个位置,然后分别向左和向右遍历,找到左右两边的最大值,然后取两个最大值的最小值,减去当前位置的高度,就是当前位置能接的雨水量,然后将所有位置的雨水量相加,就是最终的结果。

public int trap(int[] height) {
    int ans = 0;
    int size = height.length;
    for (int i = 1; i < size - 1; i++) {
        int max_left = 0, max_right = 0;
        // 
        for (int j = i; j >= 0; j--) { //Search the left part for max bar size
            max_left = Math.max(max_left, height[j]);
        }
        for (int j = i; j < size; j++) { //Search the right part for max bar size
            max_right = Math.max(max_right, height[j]);
        }
        ans += Math.min(max_left, max_right) - height[i];
    }
    return ans;
}

复杂度分析

时间复杂度:O(n^2)。两个 for 循环。

空间复杂度:O(1)。使用了常数个变量。

版本二 优化

上面的代码是暴力解法,我们可以使用动态规划的思想,将每个位置的左右最大值都计算出来,然后再遍历一次,这样就可以将时间复杂度降低到 O(n)。

public int trap(int[] height) {
    if (height == null || height.length == 0) {
        return 0;
    }
    int ans = 0;
    int size = height.length;
    int[] left_max = new int[size];
    int[] right_max = new int[size];

    left_max[0] = height[0];
    for (int i = 1; i < size; i++) {
        left_max[i] = Math.max(height[i], left_max[i - 1]);
    }

    right_max[size - 1] = height[size - 1];
    for (int i = size - 2; i >= 0; i--) {
        right_max[i] = Math.max(height[i], right_max[i + 1]);
    }

    for (int i = 1; i < size - 1; i++) {
        ans += Math.min(left_max[i], right_max[i]) - height[i];
    }
    return ans;
}

复杂度分析

时间复杂度:O(n)。两个 for 循环。

空间复杂度:O(n)。使用了两个数组。

版本三 双指针

上面的解法中,我们使用了两个数组,分别存储了每个位置的左右最大值,其实我们可以使用两个指针,分别指向当前位置的左右两边,然后再遍历一次,这样就可以将空间复杂度降低到 O(1)。

public int trap(int[] height) {
    int left = 0, right = height.length - 1;
    int ans = 0;
    int left_max = 0, right_max = 0;
    while (left < right) {
        if (height[left] < height[right]) {
            if (height[left] >= left_max) {
                left_max = height[left];
            } else {
                ans += (left_max - height[left]);
            }
            ++left;
        } else {
            if (height[right] >= right_max) {
                right_max = height[right];
            } else {
                ans += (right_max - height[right]);
            }
            --right;
        }
    }
    return ans;
}

复杂度分析

时间复杂度:O(n)。一次遍历。

空间复杂度:O(1)。使用了常数个变量。

参考

接雨水