【leetcode】42. 接雨水
题目内容
思路分析
本题的总体思路为从第二个柱子起,判断每个柱子左右两边柱子的最大高度,然后max(0, min(左右两边的最大高度)-当前柱子高度)即为当前柱子可以装的水量。
方法一:动态规划
参考资料(其中的解法3)
利用当前柱子及其左边所有柱子的最大高度max_left_height=max(当前柱子高度, 当前柱子左边一根的max_left_height)的特性,使用一个大小为n的数组保存所有max_left_height。
(对于右边也一样)
这样再遍历一遍所有柱子,用max(0, min(左右两边的最大高度)-当前柱子高度)即可算出每个柱子可以装的水量,最后全部相加即可得到总装水量。
方法二:动态规划 + 双指针
初步尝试
很多动态规划都可以对其所用空间进行优化。方法一使用了两个大小为O(n)的数组max_left_heights和max_right_heights。因此考虑对这两个数组进行优化。
当尝试将数组用一个变量代替时,会遇到问题:按从左到右的顺序去遍历数组,max_left_height倒好说,一直记忆上一个最大值并做更新即可,而max_right_height不可以(因为压根就不知道右边什么情况)。
这时有一种巧妙的解决办法是使用双指针从左右两个方向进行遍历。
思想
规定left/right指向当前所要遍历的柱子,left_max和right_max为left以左的最大值和right以右的最大值。
right_max
left_max __
__ | |
| |__ __?????????????????????? | |
__| |__| __| |__
left right
在某一时刻,对于left来说,left_max是可信的,而right_max是不可信的(因为不知道left到right中间的情况)。即left左边的最大值确定为left_max,右边的最大值大于等于right_max。因此,当left_max<=right_max时,可确定left处可以装的水为max(0, left_max-left)。即移动left,从左到右遍历。
(对于右边也一样)
代码
#include <vector>
#include <iostream>
using namespace std;
// 动态规划
// 时间复杂度O(n)
// 空间复杂度O(n)
class Solution1 {
public:
int trap(vector<int>& height) {
int n = height.size();
if (n <= 2) return 0;
int capacity{ 0 };
// i左/右侧的最大高度
vector<int> left_max_heights(n), right_max_heights(n);
// 边界条件
left_max_heights[0] = height[0];
right_max_heights[n - 1] = height[n - 1];
for (int i = 1; i <= n - 1; ++i) {
// max(第i-1个左边的最大高度,第i个高度) -> 第i个左边的最大高度
left_max_heights[i] = max(left_max_heights[i - 1], height[i]);
}
for (int i = n - 2; i >= 0; --i) {
// max(第i-1个右边的最大高度,第i个高度) -> 第i个右边的最大高度
right_max_heights[i] = max(right_max_heights[i + 1], height[i]);
}
for (int i = 1; i <= n - 1; ++i) {
// 没有和0比较是因为left_max_heights/right_max_heights已经涵盖了当前柱
// 子的大小
capacity += min(left_max_heights[i], right_max_heights[i])
- height[i];
}
return capacity;
}
};
// 动态规划 + 双指针
// 时间复杂度O(n)
// 空间复杂度O(1)
class Solution {
public:
int trap(vector<int>& height) {
int n = height.size();
if (n <= 2) return 0;
int left_max_height = height[0];
int right_max_height = height[n - 1];
int left = 1, right = n - 2;
int capacity = 0;
while (left <= right) {
if(left_max_height <= right_max_height) {
capacity += max(0, left_max_height - height[left]);
left_max_height = max(left_max_height, height[left]);
++left;
} else {
capacity += max(0, right_max_height - height[right]);
right_max_height = max(right_max_height, height[right]);
--right;
}
}
return capacity;
}
};
int main() {
Solution s;
vector<int> input{ 0,1,0,2,1,0,1,3,2,1,2,1 };
int output;
output = s.trap(input);
return output;
}
收获
- 掌握了“动态规划一般可以优化空间”的思想
- 终于解决了拖了很久的接雨水问题