题目
给定 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 个单位的雨水(蓝色部分表示雨水)。
题解
方式一:动态规划
i位置储水量 = Math.min(leftMax, rightMax) - height[i]
正常方式是向i的左右两边扫描,这样会重复计算,所以使用动态规划记录每个位置左右的最大值
public int trap(int[] height) {
int n = height.length;
int[] leftMax = new int[n]; // 左边最大值
int[] rightMax = new int[n]; // 右边最大值
leftMax[0] = height[0];
for (int i = 1; i < n; i++) {
leftMax[i] = Math.max(leftMax[i - 1], height[i]);
}
rightMax[n - 1] = height[n - 1];
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;
}
方式二:单调栈
蓄水需要洼地,栈中保存下坡部分,遇到上坡就在栈中找洼地
public int trap(int[] height) {
Stack<Integer> stack = new Stack();
int ans = 0;
for (int i = 0; i < height.length; i++) {
while (!stack.isEmpty() && height[i] > height[stack.peek()]) {
int top = stack.pop();
if (stack.isEmpty()) {
// 不构成洼地,没有下坡
break;
}
// 计算洼地蓄水量
int left = stack.peek();
int w = i - left - 1;
int h = Math.min(height[left], height[i]);
ans += w * h;
}
// 以height[i]为上坡的洼地已经计算完了
stack.push(i);
}
return ans;
}
方式三:双指针
动态规划使用两个数组可以用两个指针优化
public int trap(int[] height) {
int left = 0, right = height.length - 1; // 左右指针
int leftMax = 0, rightMax = 0; // 左右边界
int result = 0;
while (left < right) {
if (height[left] < height[right]) {
if (height[left] >= leftMax) {
leftMax = height[left];
} else {
result += leftMax - height[left];
}
left++;
} else {
if (height[right] >= rightMax) {
rightMax = height[right];
} else {
result += rightMax - height[right];
}
right--;
}
}
return result;
}
总结
算法:动态规划
、双指针
数据结构:单调栈