主题 4:攒青豆
现有 n 个宽度为 1 的柱子,给出 n 个非负整数依次表示柱子的高度,排列后如下图所示,此时均匀从上空向下撒青豆,计算按此排列的柱子能接住多少青豆。(不考虑边角堆积)
典型的接雨水问题:使用 Java 解析
public class Main {
public static void main(String[] args) {
int[] height = {5,0,2,1,4,0,1,0,3};
System.out.println(new Main().calculate(height));
}
public int calculate(int[] height){
if(height.length < 2){
return 0;
}
int qingdou = 0;
for (int cursor = 0; cursor < height.length-1; cursor++) {
// find the height after current which has the min diff with cursor
int minDiff = Integer.MAX_VALUE;
int minDiffIndex = -1;
for (int i = cursor + 1; i < height.length; i++) {
int diff = Math.abs(height[cursor] - height[i]);
if(diff < minDiff){
minDiff = diff;
minDiffIndex = i;
}
}
// calculate the qingdou
int min = Math.min(height[cursor], height[minDiffIndex]);
for (int i = cursor + 1; i < minDiffIndex; i++) {
qingdou += min - height[i];
}
cursor = minDiffIndex - 1;
}
return qingdou;
}
}
这个是常见的通过计算最小差。只是为了图块,标准解法如下5个
- 双指针法
双指针法是解决该问题的常用方法。我们首先找到数组中的最高柱子,然后从两侧向最高柱子所在位置分别扫描。在扫描的过程中,我们维护两个指针 left 和 right 分别指向数组的左右两端,以及两个变量 l_max 和 r_max 分别表示左右两侧当前扫描到的最高柱子高度。
- 初始化
在开始扫描之前,我们需要先对指针和变量进行初始化。具体来说,我们让 left 和 right 分别指向数组的两端,l_max 和 r_max 初始值均为 0。
- 扫描过程
在扫描的过程中,我们可以使用 while 循环来实现。在每次循环中,我们首先比较 height[left] 和 height[right] 的大小,将 l_max 和 r_max 中的最小值更新为当前较大的值。然后,如果 height[left] < height[right],则说明左侧的最高柱子不如右侧的最高柱子高,我们可以将 left 指针右移一位,并计算当前位置的雨水量(即 l_max - height[left]),将其加入总雨水量 ans 中。反之,如果 height[left] >= height[right],则说明右侧的最高柱子不如左侧的最高柱子高,我们可以将 right 指针左移一位,并计算当前位置的雨水量(即 r_max - height[right]),将其加入总雨水量 ans 中。
- 返回结果
在循环结束后,我们可以得到能接的雨水总量 ans,将其作为结果返回即可。
- 总结
双指针法是一种常用的解决该问题的方法,其时间复杂度为 O(n),空间复杂度为 O(1),非常高效。通过该方法,我们可以在不使用额外空间的情况下,求解出该问题的答案
对于该问题的复杂度分析如下:
时间复杂度:O(n)
- 在双指针法中,我们只需要遍历一次整个数组即可,因此时间复杂度为 O(n)。
空间复杂度:O(1)
- 在双指针法中,我们只需要使用常数个变量来维护当前的状态,因此空间复杂度为 O(1)。
综上,双指针法是一种时间复杂度为 O(n),空间复杂度为 O(1) 的高效解决该问题的方法。