42. 接雨水

双指针

image.png
按列求雨水数量,寻找当前列左右的最大高度,如何取左右高度中较小的高度-当前列高度

  1. class Solution {
  2. public:
  3. int trap(vector<int>& height) {
  4. int sum = 0;
  5. for (int i = 0; i < height.size(); i++) {
  6. // 第一个柱子和最后一个柱子不接雨水
  7. if (i == 0 || i == height.size() - 1) continue;
  8. int rHeight = height[i]; // 记录右边柱子的最高高度
  9. int lHeight = height[i]; // 记录左边柱子的最高高度
  10. for (int r = i + 1; r < height.size(); r++) {
  11. if (height[r] > rHeight) rHeight = height[r];
  12. }
  13. for (int l = i - 1; l >= 0; l--) {
  14. if (height[l] > lHeight) lHeight = height[l];
  15. }
  16. int h = min(lHeight, rHeight) - height[i];
  17. if (h > 0) sum += h;
  18. }
  19. return sum;
  20. }
  21. };

上述因为重复寻找最大高度,所以会超时,使用两个变量来记录左右的最大高度

  1. class Solution {
  2. public:
  3. int trap(vector<int>& height)
  4. {
  5. int left = 0, right = height.size() - 1;
  6. int ans = 0;
  7. int left_max = 0, right_max = 0;
  8. while (left < right) {
  9. //当左边高度小于右边高度时,当前列的水滴数与坐边的最大高度有关
  10. if (height[left] < height[right]) {
  11. //如果当前列的高度大于左边的最大高度,更新左边的最大高度
  12. //反之,计算水滴数相当于min(lHeight, rHeight) - height[i]
  13. height[left] >= left_max ? (left_max = height[left]) : ans += (left_max - height[left]);
  14. ++left;
  15. }
  16. else {
  17. height[right] >= right_max ? (right_max = height[right]) : ans += (right_max - height[right]);
  18. --right;
  19. }
  20. }
  21. return ans;
  22. }
  23. };

动态规划

转移方程
当前位置,左边的最高高度是前一个位置的左边最高高度和本高度的最大值。
即从左向右遍历:maxLeft[i] = max(height[i], maxLeft[i - 1]);
从右向左遍历:maxRight[i] = max(height[i], maxRight[i + 1]);

  1. class Solution {
  2. public:
  3. int trap(vector<int>& height) {
  4. if (height.size() <= 2) return 0;
  5. vector<int> maxLeft(height.size(), 0);
  6. vector<int> maxRight(height.size(), 0);
  7. int size = maxRight.size();
  8. // 记录每个柱子左边柱子最大高度
  9. maxLeft[0] = height[0];
  10. for (int i = 1; i < size; i++) {
  11. maxLeft[i] = max(height[i], maxLeft[i - 1]);
  12. }
  13. // 记录每个柱子右边柱子最大高度
  14. maxRight[size - 1] = height[size - 1];
  15. for (int i = size - 2; i >= 0; i--) {
  16. maxRight[i] = max(height[i], maxRight[i + 1]);
  17. }
  18. // 求和
  19. int sum = 0;
  20. for (int i = 0; i < size; i++) {
  21. int count = min(maxLeft[i], maxRight[i]) - height[i];
  22. if (count > 0) sum += count;
  23. }
  24. return sum;
  25. }
  26. };

单调栈

  1. 当前列高度小于等于栈顶高度时,将当前高度进栈
  2. 反之将当前列高度、栈顶元素和栈顶上一个元素形成凹槽,计算雨水

image.png

  1. class Solution {
  2. public:
  3. int trap(vector<int>& height) {
  4. stack<int> st;
  5. st.push(0);
  6. int sum = 0;
  7. for (int i = 1; i < height.size(); i++) {
  8. while (!st.empty() && height[i] > height[st.top()]) {
  9. int mid = st.top();
  10. st.pop();
  11. if (!st.empty()) {
  12. int h = min(height[st.top()], height[i]) - height[mid];
  13. int w = i - st.top() - 1;
  14. sum += h * w;
  15. }
  16. }
  17. st.push(i);
  18. }
  19. return sum;
  20. }
  21. };