题目:
给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。
思路:
(1)按照列方向来计算雨水面积:
当前列雨水面积:min(左边柱子的最高高度,记录右边柱子的最高高度) - 当前柱子高度
(2)按照行方向来计算雨水面积
解答:
方法一:双指针法,超时
时间复杂度:O(n^2)
空间复杂度:O(1)
class Solution:
def trap(self, height: List[int]) -> int:
#双指针法
n=len(height)
res=0
#最左边和最右边的柱子不接雨水
for i in range(1,n-1):
lh=height[i]
rh=height[i]
#找当前柱子左侧最高的柱子
for j in range(i-1,-1,-1):
if height[j]>lh:
lh=height[j]
#找当前柱子右侧最高的柱子
for j in range(i+1,n):
if height[j]>rh:
rh=height[j]
h=min(lh,rh)-height[i]
if h>0:
res+=h
return res
方法二:动态规划法,使用两个数组来分别记录当前柱子 左侧和右侧 最高柱子的高度
时间复杂度:O(n)
空间复杂度:O(n)
class Solution:
def trap(self, height: List[int]) -> int:
#动态规划法
n=len(height)
if n<=2:
return 0
res=0
#lh,rh分别记录:当前柱子 左侧最高的柱子和右侧最高柱子 的高度
lh=[0]*n
rh=[0]*n
#找当前柱子左侧最高的柱子
lh[0]=height[0]
for i in range(1,n):
lh[i]=max(height[i],lh[i-1])
#找当前柱子右侧最高的柱子
rh[n-1]=height[n-1]
for j in range(n-2,-1,-1):
rh[j]=max(height[j],rh[j+1])
#最左边和最右边的柱子不接雨水
for i in range(1,n-1):
h=min(lh[i],rh[i])-height[i]
if h>0:
res+=h
return res
方法三:单调栈:保持栈内元素有序,按照行方向来计算雨水
- 栈顶到栈底 递增,方便判断是否出现凹槽;
- 若当前柱子高度大于栈头元素,说明出现了凹槽。栈头元素就是凹槽底部的柱子,栈头第二个元素就是凹槽左边的柱子,而添加的元素就是凹槽右边的柱子;
- 求宽度的时候 如果遇到相同高度的柱子,需要使用最右边的柱子来计算宽度;
class Solution:
def trap(self, height: List[int]) -> int:
#单调栈
n=len(height)
if n<=2:
return 0
res=0
stack=[0]
for i in range(1,n):
top=stack[-1]
if height[i]<height[top]:
stack.append(i)
#高度相同,则用最右侧的高度
elif height[i]==height[top]:
stack.pop() //可有可无
stack.append(i)
#出现凹槽
else:
while stack and height[i]>height[stack[-1]]:
mid=stack.pop()
if stack:
left=height[stack[-1]]
right=height[i]
h=min(left,right)-height[mid]
w=i-stack[-1]-1
res+=h*w
stack.append(i)
return res
方法四:单调栈简化
class Solution:
def trap(self, height: List[int]) -> int:
#单调栈,按行计算雨水
n=len(height)
if n<=2:
return 0
res=0
stack=[0]
for i in range(1,n):
#出现凹槽
while stack and height[i]>height[stack[-1]]:
mid=stack.pop()
if stack:
left=height[stack[-1]]
right=height[i]
h=min(left,right)-height[mid]
w=i-stack[-1]-1
res+=h*w
stack.append(i)
return res