描述
给定 n
个非负整数表示每个宽度为 1
的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。
示例 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 个单位的雨水(蓝色部分表示雨水)。
示例 2:
输入:height = [4,2,0,3,2,5] 输出:9
提示:
n == height.length
1 <= n <= 2 * 104
0 <= height[i] <= 105
碎碎念:此题是盛最多水的容器一题的升级版,存在左右边界的都可作为容器来接水。雨水的体积即为形成容器的体积 -- 容器内部的所有heiht[i],跟俄罗斯方块有一拼哈哈
前后缀分解
class Solution:
def trap(self, height: List[int]) -> int:
# 获取高度数组的长度
n = len(height)
# 初始化 pre_max 数组,存储每个位置左边的最大高度
pre_max = [0] * n
# 第一个位置的左边最大高度就是它本身
pre_max[0] = height[0]
# 遍历数组,从左向右更新 pre_max 数组
for i in range(1, n):
pre_max[i] = max(pre_max[i - 1], height[i])
# 初始化 suf_max 数组,存储每个位置右边的最大高度
suf_max = [0] * n
# 最后一个位置的右边最大高度就是它本身
suf_max[-1] = height[-1]
# 遍历数组,从右向左更新 suf_max 数组
for i in range(n - 2, -1, -1):
suf_max[i] = max(suf_max[i + 1], height[i])
# 初始化结果变量 ans 用于存储总的雨水量
ans = 0
# 使用 zip 函数同时遍历 height、pre_max 和 suf_max 数组
for h, pre, suf in zip(height, pre_max, suf_max):
# 每个位置的积水量是左边最大高度和右边最大高度的最小值减去当前高度
ans += min(pre, suf) - h
# 返回计算出的总雨水量
return ans
整体分析
这段代码通过预计算每个位置的左边最大高度和右边最大高度,然后利用这些信息计算每个位置可能的积水量。具体步骤如下:
1. 初始化
- 获取高度数组的长度:变量
n
保存高度数组的长度。 - 初始化 pre_max 数组:
pre_max
用于存储每个位置左边的最大高度,长度与高度数组相同,初始值为 0。 - 初始化 suf_max 数组:
suf_max
用于存储每个位置右边的最大高度,长度与高度数组相同,初始值为 0。
2. 计算左边最大高度
- 第一个位置:第一个位置的左边最大高度就是它本身。
- 从左向右遍历:从第二个位置开始,依次更新
pre_max
数组,每个位置的左边最大高度是当前位置和前一个位置左边最大高度的较大值。
3. 计算右边最大高度
- 最后一个位置:最后一个位置的右边最大高度就是它本身。
- 从右向左遍历:从倒数第二个位置开始,依次更新
suf_max
数组,每个位置的右边最大高度是当前位置和后一个位置右边最大高度的较大值。
4. 计算积水量
- 初始化结果变量 ans:用于存储总的雨水量。
- 遍历每个位置:使用
zip
函数同时遍历height
、pre_max
和suf_max
数组。- 计算积水量:每个位置的积水量是左边最大高度和右边最大高度的较小值减去当前位置的高度。
- 累加积水量:将每个位置的积水量累加到结果变量
ans
中。
5. 返回结果
- 返回总的雨水量:最终返回计算出的总雨水量
ans
。
这种方法通过预计算左边和右边的最大高度,简化了后续的积水量计算过程,是解决雨水陷阱问题的一种经典算法。
相向双指针
class Solution:
# 定义一个方法 trap,接收一个整数列表 height 作为参数
def trap(self, height: List[int]) -> int:
# 初始化结果变量 ans 用于存储总的雨水量
# 初始化左右指针和两个变量 pre_max 和 suf_max 分别用于存储左边和右边的最大高度
ans = left = pre_max = suf_max = 0
# 初始化右指针为数组的最后一个索引
right = len(height) - 1
# 当左指针小于右指针时,继续循环
while left < right:
# 更新左边的最大高度
pre_max = max(pre_max, height[left])
# 更新右边的最大高度
suf_max = max(suf_max, height[right])
# 如果左边的最大高度小于右边的最大高度
if pre_max < suf_max:
# 累加左边的雨水量,即左边的最大高度减去当前左指针的高度
ans += pre_max - height[left]
# 左指针向右移动一位
left += 1
else:
# 累加右边的雨水量,即右边的最大高度减去当前右指针的高度
ans += suf_max - height[right]
# 右指针向左移动一位
right -= 1
# 返回计算出的总雨水量
return ans
整体分析
该解决方案采用双指针法,通过从数组的两端同时遍历,并维护两个变量来记录遍历过程中遇到的最大高度,从而计算每个位置可能的雨水量。双指针法通过维护从两端向中间遍历的最大高度来计算积水量。在每一步中,总是优先移动较低的一端,因为较低的一端决定了积水的高度。具体步骤如下:
-
初始化:
- 结果变量
ans
初始化为 0,用于存储总的雨水量。 left
和right
分别初始化为数组的起始和结束索引,用于从两端向中间遍历。pre_max
和suf_max
分别初始化为 0,用于记录遍历过程中左边和右边的最大高度。
- 结果变量
-
循环遍历:
- 当
left
小于right
时,进入循环。 - 更新
pre_max
为当前pre_max
和height[left]
中的较大值,即左边的最大高度。 - 更新
suf_max
为当前suf_max
和height[right]
中的较大值,即右边的最大高度。 - 比较
pre_max
和suf_max
,决定从哪一边计算雨水量:- 如果
pre_max
小于suf_max
,则当前积水量由pre_max
决定,累加pre_max
和height[left]
之差,并将left
向右移动。 - 如果
pre_max
大于或等于suf_max
,则当前积水量由suf_max
决定,累加suf_max
和height[right]
之差,并将right
向左移动。
- 如果
- 当
-
返回结果:
- 当遍历结束后,返回计算出的总雨水量
ans
。
- 当遍历结束后,返回计算出的总雨水量
单调栈
class Solution:
# 定义一个方法 trap,接收一个整数列表 height 作为参数
def trap(self, height: List[int]) -> int:
# 初始化结果变量 ans,用于存储总的雨水量
ans = 0
# 初始化一个栈 st,用于存储索引
st = []
# 枚举 height 数组中的每一个元素,i 是索引,h 是高度值
for i, h in enumerate(height):
# 当栈不为空并且当前高度大于等于栈顶元素所指的高度时
while st and h >= height[st[-1]]:
# 取出栈顶元素的高度作为底部高度
bottom_h = height[st.pop()]
# 如果栈为空,则跳出循环
if not st:
break
# 取出新的栈顶元素的索引作为左边界
left = st[-1]
# 计算当前的高度差
dh = min(height[left], h) - bottom_h
# 计算并累加雨水量:高度差乘以宽度(索引差)
ans += dh * (i - left - 1)
# 将当前索引压入栈中
st.append(i)
# 返回计算出的总雨水量
return ans
整体分析
这个解决方案使用了单调栈的思路来解决雨水陷阱问题。其核心思想是通过维护一个单调递减栈,当遇到高于栈顶元素的高度时,计算可能形成的雨水量。具体步骤如下:
- 初始化:首先,结果变量
ans
被初始化为 0,栈st
被初始化为空。 - 遍历高度数组:通过
enumerate
遍历数组height
,获取每个高度的索引i
和高度值h
。 - 维护单调递减栈:在遍历过程中,维持栈的单调递减性质。如果当前高度
h
大于等于栈顶高度,表示可以形成一个凹槽,这时就开始计算雨水量。- 计算雨水量:当栈不为空且当前高度
h
大于等于栈顶高度时,弹出栈顶元素并计算底部高度bottom_h
。如果栈为空,表示左边界不存在,跳出循环。 - 找到左边界:取新的栈顶元素的索引作为左边界。
- 计算高度差:高度差
dh
是当前高度和左边界高度的最小值减去底部高度。 - 累加雨水量:雨水量是高度差
dh
乘以宽度(即当前索引和左边界索引之差减去 1)。
- 计算雨水量:当栈不为空且当前高度
- 压入栈中:当前索引
i
被压入栈中,继续下一个元素的遍历。 - 返回结果:最后返回计算出的总雨水量
ans
。