接雨水
文章目录
- 接雨水
- 1 题目描述
- 2 分析
- 2.1 左到右
- 2.2 右到左
- 2.3 计算面积
- 3 代码
- 3.1 Java
- 3.2 Python
- 附录1
1 题目描述
https://leetcode.cn/problems/trapping-rain-water/
面试的时候关键不是你的手法多么精妙,首先要做出来。
给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。
输入:height = [0,1,0,2,1,0,1,3,2,1,2,1]
输出:6
解释:上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图,在这种情况下,可以接 6 个单位的雨水(蓝色部分表示雨水)。
输入:height = [4,2,0,3,2,5]
输出:9
2 分析
通过观察,我们知道,要计算接的水,我们要找到能够存水的凹槽:
我们将这些存水的凹槽的边缘连接起来,可以发现,折线图的总体趋势是从低到高,从高到低(下图是随机生成的不同高度下的存水凹槽边缘图,以及其连线)
绘图代码已经在附录1中给出,可以直接运行,随机生成数据以及查看绘图。
那么我们能做的就是从左到右,依次找到越来越高的值;保存其索引,从右到左,依次找到越来越高的值,保存其索引。然后计算这些索引之间的最大盛水面积,减去之间的柱子的面积,就是最后的接雨水的结果。
2.1 左到右
while start < len(height) and height[start] == 0: # 指针不断右移,跳过为0的元素
start = start + 1
if start >= len(height) - 1: # 要是全为0,return 0
return 0
lists.append(height[start]) # 保存值
indexes.append(start) # 保存索引
for i in range(start + 1, len(height)):
if height[i] >= lists[-1]: # 比上一个凹槽边缘大或者等于,保存
lists.append(height[i])
indexes.append(i)
2.2 右到左
# 注意从右到左,不要超过左到右的边界indexes[-1],
while end > indexes[-1] and height[end] == 0:
end = end - 1
# 而且从左到右已经判断完全0了,接下来不用判断了
reverse_lists.append(height[end])
reverse_indexes.append(end)
for i in range(end - 1, indexes[-1] - 1, -1): # 从右到左,注意边界
# 注意range(end - 1, indexes[-1] - 1, -1),举个例子,如果要生成一个10~0的
# 序列,则为range(10, -1, -1),我们知道range的前两个参数为范围,前闭后开,[10,-1)
# 第二个-1为step,表示按照反向遍历。
if height[i] >= reverse_lists[-1]:
reverse_lists.append(height[i])
reverse_indexes.append(i)
2.3 计算面积
最直接的想法是,直接分开计算面积:
l_res = 0
for i in range(1, len(lists)):
l = indexes[i - 1]
r = indexes[i]
l_res = l_res + (r - l - 1) * (min(height[l], height[r])) # 高度最小值乘以长度,
# 长度为(right - left - 1)
for j in range(l + 1, r):
l_res = l_res - height[j] # 减去中间的面积
for i in range(len(reverse_indexes) - 2, -1, -1): # 反过来遍历
r = reverse_indexes[i]
l = reverse_indexes[i + 1]
l_res = l_res + (r - l - 1) * (min(height[l], height[r]))
for j in range(l + 1, r):
l_res = l_res - height[j]
return l_res
为了简单计算,首先我们将两个索引合并,
l_res = 0
# 将indexes和reversed(reverse_indexes)合并
indexes.extend(reversed(reverse_indexes))
在上面的图中,我们看到,两边的索引(左到右:[0, 3, 5],右到左:[19, 16, 11, 7, 5])有重叠,合并以后变成了[0, 3, 5, 5, 7, 11, 16, 19]怎么办?
无所谓,重叠之后长度为0,二者之间的面积还是0,不会对最终的面积有啥影响。
那么最后计算面积的过程为:
for i in range(1, len(indexes)):
l = indexes[i - 1]
r = indexes[i]
l_res = l_res + max((r - l - 1), 0) * (min(height[l], height[r]))
for j in range(l + 1, r):
l_res = l_res - height[j]
return l_res
3 代码
3.1 Java
class Solution {
public int trap(int[] height) {
if (height.length <= 1) return 0;
List<Integer> lists = new ArrayList<>();
List<Integer> indexes = new ArrayList<>();
List<Integer> reverse_lists = new ArrayList<>();
List<Integer> reverse_indexes = new ArrayList<>();
int start = 0;
int end = height.length - 1;
while(start < height.length && height[start] == 0) start++;
if (start >= height.length - 1) return 0;
lists.add(height[start]);
indexes.add(start);
for (int i = start + 1; i < height.length; i++) {
if (height[i] >= lists.get(lists.size() - 1)) {
lists.add(height[i]);
indexes.add(i);
}
}
int l_res = 0;
for (int i = 1; i < lists.size(); i++) {
int l = indexes.get(i - 1);
int r = indexes.get(i);
l_res += (r - l - 1) * (Math.min(height[l], height[r]));
for (int j = l + 1; j < r; j++) {
l_res -= height[j];
}
}
while(end > indexes.get(indexes.size() - 1) && height[end] == 0) end--;
reverse_lists.add(height[end]);
reverse_indexes.add(end);
for (int i = end - 1; i >= indexes.get(indexes.size() - 1); i--) {
if (height[i] >= reverse_lists.get(reverse_lists.size() - 1)) {
reverse_lists.add(height[i]);
reverse_indexes.add(i);
}
}
for (int i = reverse_indexes.size() - 2; i >=0; i--) {
int r = reverse_indexes.get(i);
int l = reverse_indexes.get(i + 1);
l_res += (r - l - 1) * (Math.min(height[l], height[r]));
for (int j = l + 1; j < r; j++) {
l_res -= height[j];
}
}
return l_res;
}
}
3.2 Python
class Solution(object):
# python 代码
def trap(self, height):
"""
:type height: List[int]
:rtype: int
"""
if len(height) <= 1:
return 0
lists = []
indexes = []
reverse_lists = []
reverse_indexes = []
start = 0
end = len(height) - 1
while start < len(height) and height[start] == 0:
start = start + 1
if start >= len(height) - 1:
return 0
lists.append(height[start])
indexes.append(start)
for i in range(start + 1, len(height)):
if height[i] >= lists[-1]:
lists.append(height[i])
indexes.append(i)
while end > indexes[-1] and height[end] == 0:
end = end - 1
reverse_lists.append(height[end])
reverse_indexes.append(end)
for i in range(end - 1, indexes[-1] - 1, -1):
if height[i] >= reverse_lists[-1]:
reverse_lists.append(height[i])
reverse_indexes.append(i)
l_res = 0
# 将indexes和reversed(reverse_indexes)合并
indexes.extend(reversed(reverse_indexes))
for i in range(1, len(indexes)):
l = indexes[i - 1]
r = indexes[i]
l_res = l_res + max((r - l - 1), 0) * (min(height[l], height[r]))
for j in range(l + 1, r):
l_res = l_res - height[j]
return l_res
附录1
# python 代码
def trap(height):
"""
:type height: List[int]
:rtype: int
"""
if len(height) <= 1:
return 0
lists = []
indexes = []
reverse_lists = []
reverse_indexes = []
start = 0
end = len(height) - 1
while start < len(height) and height[start] == 0:
start = start + 1
if start >= len(height) - 1:
return 0
lists.append(height[start])
indexes.append(start)
for i in range(start + 1, len(height)):
if height[i] >= lists[-1]:
lists.append(height[i])
indexes.append(i)
while end > indexes[-1] and height[end] == 0:
end = end - 1
reverse_lists.append(height[end])
reverse_indexes.append(end)
for i in range(end - 1, indexes[-1] - 1, -1):
if height[i] >= reverse_lists[-1]:
reverse_lists.append(height[i])
reverse_indexes.append(i)
l_res = 0
# 将indexes和reversed(reverse_indexes)合并
history_indexes = indexes.copy()
indexes.extend(reversed(reverse_indexes))
for i in range(1, len(indexes)):
l = indexes[i - 1]
r = indexes[i]
l_res = l_res + max((r - l - 1), 0) * (min(height[l], height[r]))
for j in range(l + 1, r):
l_res = l_res - height[j]
return l_res, history_indexes, reverse_indexes
def draw_pic(water, indexes, reverse_indexes):
indexes.extend(reverse_indexes)
indexes = list(set(indexes))
indexes.sort()
# 绘制折线图,x轴为indexes,y轴为water[indexes]
plt.plot(indexes, [water[i] for i in indexes])
plt.scatter(indexes, [water[i] for i in indexes])
# 将water绘制成柱状图
plt.bar(range(len(water)), water, color='yellow', zorder=1, edgecolor='black', linewidth=1)
# 将indexes绘制成柱状图,绿色
plt.bar(indexes, [water[i] for i in indexes], color='g', zorder=100, edgecolor='black', linewidth=1)
# 设置 x 轴刻度及标签
plt.xticks(np.arange(0, 20), range(0, 20))
plt.show()
# 生成一个更长的测试用例
import random
random.randint(0, 10)
water = [random.randint(0, 10) for _ in range(20)]
res = trap(water)
indexes = res[1]
reverse_indexes = res[2]
draw_pic(water, indexes, reverse_indexes)