【c++】【leetcode42】接雨水(动态规划、单调栈、双指针)

接雨水
在这里插入图片描述

解题思路:动态规划

每一根柱子头顶上能接多少雨水取=min(左边最高,右边最高)- 柱子本身的高度
因此只需要得到下标为i的柱子左边最高的left_max和右边最高的right_max的值就可以

  • ans[i] = min(left_max[i],right_max[i]) - height[i]

如果是每一个柱子都去遍历寻找,时间复杂度是 O ( n 2 ) O(n^2) O(n2),这里可以借助动态规划去保存已经寻找过的信息。

动态规划:

  • 初始化: left_max[0] = height[0]; right_max[n-1] = height[n-1];
  • 动态转移方程:
    • left_max[i] = max(left_max[i-1],height[i]);//从左往右
    • right_max[j] = max(right_max[j+1],height[j]);//从右往左

时间复杂度 O ( n ) O(n) O(n),空间复杂度 O ( n ) O(n) O(n)

代码

class Solution {
public:
    int trap(vector<int>& height) {
        int n = height.size();
        int ans = 0;
        int left_max[n],right_max[n];
        left_max[0] = height[0];//初始化
        right_max[n-1] = height[n-1];//初始化
        for(int i = 1,j = n - 2;i < n,j >= 0;++i,--j){
            left_max[i] = max(left_max[i-1],height[i]);//转移方程
            right_max[j] = max(right_max[j+1],height[j]);//转移方程
        }
        for(int i = 0;i < n;++i){
            ans += min(left_max[i],right_max[i]) - height[i];//计算每一步的结果
        }
        return ans;
    }
};

解题思路:单调栈

用单调栈去记录下标:
维护一个递减的栈,当遇到第一个大于栈顶的元素,此时去计算能接的雨水量。
在这里插入图片描述
时间复杂度 O ( n ) O(n) O(n),空间复杂度 O ( n ) O(n) O(n)

代码

class Solution {
public:
    int trap(vector<int>& height) {
        int n = height.size();
        stack<int> s;
        s.push(0);
        int ans = 0;
        for(int i = 1;i < n;++i){
            while(!s.empty() && height[i] > height[s.top()]){
                int top = s.top();//当前顶部
                s.pop();
                if(s.empty())break;
                int left = s.top();//顶部的前一位,大于顶部
                int wid = i - left - 1;//要计算的宽度
                int hei = min(height[left],height[i]) - height[top];//要计算的高度
                ans += wid * hei;
            }
            s.push(i);
        }
        return ans;
    }
};

解题思路:双指针

优化动态规划:
left_max、right_max分别记录左右两边的最大值,每一个柱子可以接的水依旧是min(left_max[i],right_max[i]) - height[i]

要解决的问题是:怎么样才能保证left_max、right_max分别代表左右两边的最大值?
不需要是最大值,我们只需要保证最大值里相对较小的那一个正确即可,因为左右两边我们只用到了较小那一个。
在这里插入图片描述
两个指针在同一轮里移动,会导致最后可能会跳过某个柱子,所以这里用了if-else

class Solution {
public:
    int trap(vector<int>& height) {
        int n = height.size();
        int l = 0,r = n - 1;
        int left_max = height[0],right_max = height[n-1];
        int ans = 0;

        while(l < r){
            left_max = max(height[l],left_max);
            right_max = max(height[r],right_max);
            if(left_max <= right_max){
                ans += left_max - height[l];
                l++;
            }
            else{
                ans += right_max - height[r];
                r--;
            }
        }   
        return ans;
    }
};
### 雨水问题的 C++ 实现 雨水问题是算法领域中的经典问题,其目标是计算在给定一系列高度不同的柱子之间能够储存多少单位的雨水。解决这一问题的方法包括动态规划单调栈双指针技巧。 #### 动态规划方法 动态规划是一种直观且易于理解的解决方案。其核心思想是预先记录每个位置左侧和右侧的最大高度,并根据这些信息计算每个位置能储存的水量。 ```cpp class Solution { public: int trap(vector<int>& height) { int n = height.size(); if (n == 0) return 0; vector<int> left_max(n, 0); vector<int> right_max(n, 0); int ans = 0; // 计算每个位置左边的最大高度 left_max[0] = height[0]; for (int i = 1; i < n; ++i) { left_max[i] = max(height[i], left_max[i - 1]); } // 计算每个位置右边的最大高度 right_max[n - 1] = height[n - 1]; for (int i = n - 2; i >= 0; --i) { right_max[i] = max(height[i], right_max[i + 1]); } // 根据左右最大高度计算储水量 for (int i = 0; i < n; ++i) { ans += min(left_max[i], right_max[i]) - height[i]; } return ans; } }; ``` 这种方法的时间复杂度为 $O(n)$,空间复杂度也为 $O(n)$,因为需要额外存储左右最大高度数组[^2]。 #### 单调栈方法 单调栈是一种更高效的方法,可以在一次遍历中完成计算。它通过维护一个栈来记录可能形成积水区域的柱子下标,并在遇到更高的柱子时计算积水体积。 ```cpp class Solution { public: int trap(vector<int>& height) { int ans = 0; stack<int> stk; int n = height.size(); for (int i = 0; i < n; ++i) { while (!stk.empty() && height[i] > height[stk.top()]) { int top = stk.top(); stk.pop(); if (stk.empty()) break; int distance = i - stk.top() - 1; int bounded_height = min(height[i], height[stk.top()]) - height[top]; ans += distance * bounded_height; } stk.push(i); } return ans; } }; ``` 此方法的时间复杂度为 $O(n)$,空间复杂度为 $O(n)$,主要取决于栈的大小[^3]。 #### 双指针方法 双指针方法利用两个指针从两端向中间移动,同时跟踪左右两侧的最大高度。这种方法将空间复杂度降低到 $O(1)$。 ```cpp class Solution { public: int trap(vector<int>& height) { int left = 0, right = height.size() - 1; int left_max = 0, right_max = 0; int ans = 0; while (left < right) { if (height[left] < height[right]) { if (height[left] >= left_max) { left_max = height[left]; } else { ans += left_max - height[left]; } ++left; } else { if (height[right] >= right_max) { right_max = height[right]; } else { ans += right_max - height[right]; } --right; } } return ans; } }; ``` 这种方法的时间复杂度为 $O(n)$,而空间复杂度为 $O(1)$,非常适用于内存受限的环境[^4]。 ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值