leetcode42.接雨水
题目描述
思路
动态规划:
雨水能存储多少,取决于较短的那一边的高度,所以我们可以列举出每一列左右两边最短的“木板”,从而即可求解每一列最多存储多少雨水。
时间复杂度:O(n)
空间复杂度:O(n)
class Solution {
public int trap(int[] height) {
int n = height.length;
//dp[i][0] 左边 dp[i][1]右边
int[][] dp = new int[n][2];
//dp[0][0] = 0;
//求每一列左边的最高的墙
for(int i = 1; i < n; i++) {
dp[i][0] = Math.max(height[i - 1], dp[i - 1][0]);
}
//dp[n - 1][1] = 0;
//求每一列右边最高的墙
for(int i = n - 2 ; i >= 0; i--) {
dp[i][1] = Math.max(height[i + 1], dp[i + 1][1]);
}
int ans = 0;
for(int i = 1; i < n - 1; i++) {
//每一列的存水量取决于较小的那一边的高度差
int temp = Math.min(dp[i][0] - height[i],
dp[i][1] - height[i]);
//小于0的情况即存不了雨水(会漏出去)
ans += temp < 0 ? 0 : temp;
}
return ans;
}
}
双指针解法:
- 由于每一列雨水存量取决于较短的一边,所以我们只需要让左右两边的最大值中的那个较小值是确定的,则可以得知当前列的存水量。
- 在动态规划解法中,我们左右分别遍历了一遍才得知每列左右的最大值分别是多少,即我们从左边遍历就能确保左边的最大值,从右边遍历我们就能确保右边的最大值。
- 结合上面的解题思路,我们可以得出最终方案:当左边的最大值小于右边的最大值,我们求出左指针所在的点的存水量。移动左指针,当右边的最大值小于左边的最大值,我们求出右指针所在的点的存水量,移动右指针。
时间复杂度:O(n)
空间复杂度:O(1)
class Solution {
public int trap(int[] height) {
int n = height.length;
if(n == 0) return 0;
int l = 0;
int r = n - 1;
//右最大值
int lm = height[l];
//左最大值
int rm = height[r];
int ans = 0;
while(l < r) {
if(lm < rm) {
//更新l位置指针左边的最高点
lm = Math.max(lm, height[++l]);
ans += lm - height[l];
} else {
//更新r位置指针右边的最高点
rm = Math.max(rm, height[--r]);
ans += rm - height[r];
}
}
return ans;
}
}