题目
给定一个整形数组arr,其中所有值非负
将这个数组看作一个柱子高度图,数组以外的区域高度视为0,求按此排列的柱子下雨之后能接多少雨水
解法1
对每一列,该列能存的雨水高度=min(左边最高的柱子,右边最高的柱子)-该列的高度
例如上图中 arr[1]=1
,左边最高的柱子为 arr[0]=3
,右边最高的柱子为 arr[3]=5
,则该列能存的雨水高度为 min(arr[0],arr[3]) - arr[1] = 3-1 = 2
dp
因此可以开两个dp数组,分别记录当前位置左边最高的柱子
、当前位置右边最高的柱子
从左往右
遍历一次,判断当前位置的柱子是否比 前一个位置的左边最高的柱子高,若当前的柱子高,dp值记录当前下标;否则,仍然记录 前一个位置的dp值
从右往左
遍历一次,进行类似的操作,记录每个位置右边最高的柱子
最后,从左往右遍历这两个dp数组,计算每列能存的雨水,累加
代码
class Solution:
def maxWater(self, arr):
water = 0
# dp记录每个位置左边最高的柱子和右边最高的柱子
dp = [[-1, -1] for i in range(len(arr))]
dp[0][0] = 0
for i in range(1, len(arr)):
dp[i][0] = dp[i - 1][0] if arr[i] <= arr[dp[i - 1][0]] else i
dp[-1][1] = len(arr) - 1
for j in range(len(arr) - 2, -1, -1):
dp[j][1] = dp[j + 1][1] if arr[j] <= arr[dp[j + 1][1]] else j
for k in range(len(arr)):
water += min(arr[dp[k][0]], arr[dp[k][1]]) - arr[k]
return water
解法2
思路:每发现一个u型凹槽
(高,低,高),计算该凹槽中的水量
对一个u型凹槽,水量:宽度 *(min(左高度,右高度) - 底高度)
计算后,把该凹槽填平
,继续寻找凹槽
例,有数组[4,2,0,3,2,5],画图理解整个过程:
单调栈
想要及时找到每一个u型凹槽,可以维护一个单调递减的栈
;由于本题中需要考虑凹槽的宽度
,因此栈中应当记录的是下标
若出现比栈顶元素大的元素,则(次栈顶,栈顶,当前元素)
一定构成一个凹槽,计算该凹槽中的水量,并弹出栈顶
元素
注意,弹出后,栈顶元素可能仍然比当前元素小,此时仍然构成u型槽,因此需要循环
计算水量并弹出栈顶元素;直到栈中没有比当前元素小的元素,再把当前元素入栈
遍历整个数组,不断维护单调栈,即可求得总水量
代码
class Solution: # 单调栈法
def maxWater(self, arr):
stack = []
water = 0
for i in range(len(arr)):
if not stack or arr[i] <= arr[stack[-1]]:
pass
else:
while stack and arr[i] > arr[stack[-1]]:
if len(stack) > 1:
width = i - stack[-2] - 1
water += width * (min(arr[stack[-2]], arr[i]) - arr[stack[-1]])
stack.pop()
stack.append(i)
return water
参考链接
https://blog.nowcoder.net/n/7b2ff3cdc8a44d9084288a6682eb1531?f=comment
https://blog.csdn.net/weixin_42265382/article/details/120910200