42. 接雨水
给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。
上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图,在这种情况下,可以接 6 个单位的雨水(蓝色部分表示雨水)。 感谢 Marcos 贡献此图。
示例:
输入: [0,1,0,2,1,0,1,3,2,1,2,1] 输出: 6
通过次数
88,455
提交次数
175,142
两遍扫描
class Solution:
def trap(self, height: List[int]) -> int:
n = len(height)
left_max, right_max = [-1] * n, [-1] * n
left_max[0], right_max[n-1] = height[0], height[n-1]
for i in range(1, n):
left_max[i] = max(left_max[i-1], height[i])
for i in range(n-2, -1, -1):
right_max[i] = max(right_max[i+1], height[i])
cnt = 0
for i in range(1, n - 1):
cnt += min(left_max[i], right_max[i]) - height[i]
return cnt
双指针
class Solution:
def trap(self, height: List[int]) -> int:
n = len(height)
if n <= 2:
return 0
ans = 0
left_max, right_max = height[0], height[n-1]
left, right = 1, n - 1
while left <= right:
if left_max < right_max:
left_max = max(left_max, height[left])
ans += left_max - height[left]
left += 1
else:
right_max = max(right_max, height[right])
ans += right_max - height[right]
right -= 1
return ans
单调栈
class Solution:
def trap(self, height: List[int]) -> int:
n = len(height)
if n <= 2:
return 0
ans = 0
stack = [0]
for i in range(1, n):
# if len(stack) == 0:
# stack.append(i)
# elif height[i] <= height[stack[-1]]:
# stack.append(i)
# 以下if可以丢弃
if len(stack) == 0 or height[i] <= height[stack[-1]]:
stack.append(i)
else:
while len(stack) and height[i] > height[stack[-1]]:
idx = stack.pop(-1)
if len(stack):
ans += (i - stack[-1] - 1) * (min(height[i], height[stack[-1]]) - height[idx])
stack.append(i)
return ans
法一:
暴力,时间复杂度O(n2,空间复杂度O(1),运行超时
int trap(vector<int>& height) {
int ans = 0;
for(int i=0; i<height.size(); i++){
int l_max=0, r_max=0;
for(int j=0; j<=i; j++) //必须包括i,否则当前位置接的雨水可能为负
l_max = max(height[j], l_max);
for(int j=i; j<height.size(); j++) //必须包括i,否则当前位置接的雨水可能为负
r_max = max(height[j], r_max);
ans += (min(l_max, r_max)-height[i]);
}
return ans;
}
法二:
dp(记忆化)(不必每次计算r_max和l_max)
时间复杂度O(n),空间复杂度O(n)
int trap(vector<int>& height) {
int ans = 0;
const int n = height.size();
//特判
if(0 == n) return 0;
int l_max[n];
int r_max[n];
l_max[0] = height[0];
r_max[n-1] = height[n-1];
for(int i=1; i<n; i++)
l_max[i] = max(height[i], l_max[i-1]);
for(int i=n-2; i>=0; i--)
r_max[i] = max(height[i], r_max[i+1]);
for(int i=0; i<n; i++)
ans += (min(l_max[i], r_max[i])-height[i]);
return ans;
}
法三:
双指针,时间复杂度O(n),空间复杂度O(1)
int trap(vector<int>& height) {
int ans = 0;
const int n = height.size();
//特判
if(0 == n) return 0;
int left = 0, right = n-1;
int left_max = height[0], right_max = height[n-1];
while(left <= right){
if(left_max < right_max){ //左边为短板
left_max = max(left_max, height[left]);
ans += left_max - height[left];
left++;
}
else{ //右边为短板
right_max = max(right_max, height[right]);
ans += right_max - height[right];
right--;
}
}
return ans;
}
法四:
单调栈(链接),时间复杂度O(n),空间复杂度O(1)
int trap(vector<int>& height) {
int ans = 0;
stack<int> s;
s.push(0);
for(int i=1; i<height.size(); i++){
while(!s.empty() && height[i]>height[s.top()]){
int idx = s.top();
s.pop();
while(!s.empty() && height[idx]==height[s.top()])
s.pop();
if(!s.empty())
ans += (min(height[s.top()], height[i])-height[idx]) * (i-s.top()-1);
}
s.push(i);
}
return ans;
}
所得:整体过于复杂无从下手时,可以考虑其中的一种情况(分治)