「这是我参与11月更文挑战的第12天,活动详情查看:2021最后一次更文挑战」
题目
给定 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 个单位的雨水(蓝色部分表示雨水)。
题目来源:力扣(LeetCode)
代码
动态规划
1.使用两个数组leftMax和rightMax维护以i位置上左边最大值和右边最大值(包括了i位置)
2.leftMax和rightMax中较小部分和i位置所能形成的差就是i位置能够存放的水滴值
class Solution {
public int trap(int[] height) {
if (height == null || height.length == 0) return 0;
int n = height.length;
int[] leftMax = new int[n];//左边部分
int[] rightMax = new int[n];//右边部分
leftMax[0] = height[0]; //初始化
rightMax[n - 1] = height[n - 1];
for (int i = 1; i < n; i++) {
leftMax[i] = Math.max(leftMax[i - 1], height[i]);//更新最大值
}
for (int i = n - 2; i >= 0; i--) {
rightMax[i] = Math.max(rightMax[i + 1], height[i]);//更新最大值
}
int ans = 0;
for (int i = 0; i < n; i++) {
ans += Math.min(leftMax[i], rightMax[i]) - height[i];//两边最小的是能够装水的高度
}
return ans;
}
}
双指针
前面使用动态规划的方法需使用两个额外的数组空间,空间复杂度为O(N),这边使用两个指针leftMax和rightMax来替代这两个数组,将空间复杂度优化为O(1)
class Solution {
public int trap(int[] height) {
if (height == null || height.length == 0) return 0;
int n = height.length;
int left = 0; //左指针
int right = n - 1; //右指针
int leftMax = height[left]; //维护左边最大值
int rightMax = height[right]; //维护右边最大值
int ans = 0;
while (left < right) {
leftMax = Math.max(height[left], leftMax);//更新左边最大值
rightMax = Math.max(height[right], rightMax); //更新右边最大值
if (height[left] < height[right]) { //由于经历了前两步的更新操作,这个实际上 <=> leftMax< rightMax
ans += leftMax - height[left];
left++; //此时计算完了left位置上能够装下的水滴的数量 left++ 移动
} else {
ans += rightMax - height[right];
right--; //此时计算完了right位置上能够装下的水滴的数量 right++ 移动
}
}
return ans;
}
}