返回

站在算法的肩膀,合理规划以求成功:直方图中的水量

前端

直方图,如同城市的天际线,高低错落的柱子描绘着数据的分布。但如果我们把这些柱子看作蓄水池的边界,它们之间又能容纳多少“水”呢?这个问题看似简单,却蕴含着算法设计的巧妙之处。

我们可以把问题想象成这样:给定一个由若干个柱子组成的直方图,每个柱子的宽度都为1,高度由一个数组表示。我们需要计算出这些柱子之间能够容纳的最大水量。

解决这个问题的关键在于找到每个柱子左右两侧最高的柱子。因为只有当一个柱子的左右两侧都有比它高的柱子时,它才能容纳水。而且,它能容纳的水量,是由它左右两侧最高的柱子中较矮的那个,以及它自身的高度决定的。

一种直观的解法是,对每个柱子,都遍历一遍数组,找到它左侧最高的柱子和右侧最高的柱子。这种方法的思路很简单,但时间复杂度较高,达到了O(n^2),其中n是柱子的数量。

为了提高效率,我们可以采用动态规划的思想。我们可以先创建一个数组leftMax,用来存储每个柱子左侧最高的柱子的高度。我们从左到右遍历数组,leftMax[i]的值就是max(leftMax[i-1], height[i-1]),其中height是存储柱子高度的数组。

同样的,我们再创建一个数组rightMax,用来存储每个柱子右侧最高的柱子的高度。我们从右到左遍历数组,rightMax[i]的值就是max(rightMax[i+1], height[i+1])。

有了leftMax和rightMax数组,我们就可以很容易地计算出每个柱子能够容纳的水量了。对于每个柱子i,它能容纳的水量就是min(leftMax[i], rightMax[i]) - height[i]。如果这个值小于0,说明该柱子不能容纳水,我们把它置为0。

最后,我们将所有柱子能够容纳的水量加起来,就是整个直方图能够容纳的最大水量了。

这种方法的时间复杂度只有O(n),因为它只需要遍历数组三次。空间复杂度也是O(n),因为我们需要额外的两个数组来存储leftMax和rightMax。

下面是用Java代码实现的动态规划算法:

public int trap(int[] height) {
    if (height == null || height.length < 3) {
        return 0;
    }
    int n = height.length;
    int[] leftMax = new int[n];
    int[] rightMax = new int[n];
    leftMax[0] = height[0];
    for (int i = 1; i < n; i++) {
        leftMax[i] = Math.max(leftMax[i - 1], height[i]);
    }
    rightMax[n - 1] = height[n - 1];
    for (int i = n - 2; i >= 0; i--) {
        rightMax[i] = Math.max(rightMax[i + 1], height[i]);
    }
    int water = 0;
    for (int i = 0; i < n; i++) {
        water += Math.max(0, Math.min(leftMax[i], rightMax[i]) - height[i]);
    }
    return water;
}

常见问题解答

1. 为什么需要使用动态规划来解决这个问题?

动态规划可以帮助我们避免重复计算。在计算每个柱子能容纳的水量时,我们需要知道它左右两侧最高的柱子。如果我们每次都重新计算,就会浪费很多时间。而动态规划可以帮助我们把这些信息存储起来,避免重复计算。

2. leftMax和rightMax数组的作用是什么?

leftMax数组存储了每个柱子左侧最高的柱子的高度,rightMax数组存储了每个柱子右侧最高的柱子的高度。这两个数组是动态规划算法的关键,它们帮助我们避免了重复计算。

3. 如何计算每个柱子能容纳的水量?

每个柱子能容纳的水量是由它左右两侧最高的柱子中较矮的那个,以及它自身的高度决定的。具体来说,就是min(leftMax[i], rightMax[i]) - height[i]。

4. 时间复杂度和空间复杂度是多少?

时间复杂度是O(n),空间复杂度也是O(n)。

5. 还有其他方法可以解决这个问题吗?

除了动态规划,还可以使用双指针的方法来解决这个问题。双指针方法的时间复杂度也是O(n),空间复杂度是O(1)。

希望这篇文章能够帮助你理解如何使用动态规划来解决直方图中的水量计算问题。动态规划是一种非常强大的算法,它可以用来解决很多其他的问题。掌握动态规划的思想,可以帮助你更好地理解和解决算法问题。