[Daily Practice] -接雨水问题
程序员文章站
2022-05-05 23:15:34
...
问题描述
给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。
该题目来源于leetcode,点击进入
上面是由数组 [2, 0, 1, 1, 0, 3, 2, 1, 0, 0, 4, 1, 2, 0, 2,] 表示的高度图,在这种情况下,可以接 18 个单位的雨水(绿色部分表示雨水)。
解题思路
如上图,这里我们定义一个有效水槽的概念,
要满足有效水槽,也就是两个柱之间必须能装水,且最左边的柱子不能高于最右边的柱子,那么这两个柱与他们中间的柱组成的部分即为有效水槽。
首先我们把它以最高的砖分成两部分,为什么分成两部分,在后面会讲到,如下图
我的思路是这样的,从左边往右边找,先找到一个比它低的水槽,再找到一个比它还高的位置,我们就停下来。这样,我们得到了一个有效水槽水槽。水槽必须满足如下条件,两边高,中间低,且左边比右边低。
但是如果最高的柱子在中间,那么我们再往后找就找不到有效水槽了。所以,这里把它分成两部分。对于后面部分,翻转之后即可。取出第一部分计算得到装水的单元格数量,再使用相同的方法得到第二部分翻转之后装水的单元格数量。相加即为总装水的单元格数量。
首先我们找到了第一个有效水槽,如下:
显然,这里装水的单元格的数量就等于红色部分,减去中间黑色部分。
将整个数组全部进行这样拆分,最后累加即可得解。
代码
public static int trap(int[] height) {
int n = height.length;
if (n < 2) return 0; // 小于三个柱子无法构成水槽
int entity = 0; // 水槽中间的柱子的数量
int index = 0;
while (height[index] == 0 ) index++; // 以高度为0开头的柱子无法组成水槽
int left = height[index]; // 第一个有效水槽最左边的柱子
int leftIndex = index; // 记录有效水槽最左边的柱子的下标,用于计算水槽中间格子数量
int flag = 0; // 满足水槽的条件一定是两边高,中间低,缺一不可
int count = 0; // 水槽中间部分的总单元格数
int curEntity = 0; // 记录水槽中间的柱子数量
for (int i = index + 1; i < n; i++) {
if (left > height[i]) {
flag++;// 找到比最左边低的柱子时做个标记,以便于找到比它高或者相等时判断是否为有效水槽
curEntity += height[i]; // 柱子的高度即为所占水槽单元格数
continue;
}
// 当找到比最左边柱子高或者相等时,需要判断当前水槽是否为有效水槽
if (left <= height[i]) {
// 有效水槽的情况
if (flag > 0) {
count += (i - leftIndex - 1) * left;
entity += curEntity;
// 判断当前水槽的下一个水槽的开始节点是否比当前水槽的最后一个柱子高,如果比它高的话,下一个水槽的起始柱子应该为高的那一个
if(i + 1 < n - 1 && height[i] < height[i + 1]){
i++;
}
// 重新对变量赋值
left = height[i];// 水槽的开始柱子高度
leftIndex = i; // 水槽的开始位置
flag = 0; // 重置有效水槽标记
curEntity = 0; // 重置水槽中间的柱子数量
}else{
// 非有效水槽时,将水槽的开始柱子位置向后移动
if(leftIndex+1 == i){
left = height[i];
leftIndex = i;
}
}
}
}
// 当初将水槽从最高的柱子分成了两部分,前面只计算了左边部分
int rightNum = 0;
if (left > height[n - 1]) {
int[] right = new int[n - leftIndex];
int rightIndex = 0;
// 将右边部分翻转,得到和左边一样的结构
for (int i = n - 1; i >= leftIndex; i--) {
right[rightIndex] = height[i];
rightIndex++;
}
// 翻转后的右边部分装水的单元格数量
rightNum = trap(right);
}
return count - entity + rightNum;
}