LeetCode/接雨水

给定n个非负整数表示每个宽度为1的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。

一. 接雨水I

1. 暴力求解

根据每一个柱子左右两端最高的柱子,计算其蓄水量,然后把总的加起来,时间复杂度为O(n2)

暴力双重循环
class Solution {
public:
    int trap(vector<int>& height){
        int n = height.size();
        int ans = 0;
        for(int i = 1;i<n-1;i++){
            int l_max = 0,r_max = 0;
            //找右边最高的柱子
            for(int j=i;j<n;j++)
                r_max = max(r_max,height[j]);
            //找左边最高的柱子
            for(int j=i;j>=0;j--)
                l_max = max(l_max,height[j]);
            ans += min(l_max,r_max) - height[i];
    }
        return ans;
    }
};

2.暴力求解优化

先用备忘录计算每个位置左右两端最高柱子高度,避免重复计算,时间复杂度为O(n),空间复杂度为O(n)

备忘录预处理
class Solution {
public:
    int trap(vector<int>& height){
        int n = height.size();
        int ans = 0;
        vector<int> rmax(n+1);
        vector<int> lmax(n+1);
        for(int i=0;i<n;i++){
            lmax[i+1] = max(lmax[i],height[i]);//计算左侧最大值
            rmax[n-i-1] = max(rmax[n-i],height[n-i-1]);//计算右侧最大值
            }
        for(int i = 1;i<n-1;i++)
            ans += min(lmax[i+1],rmax[i]) - height[i];
        return ans;
    }
};

3.双指针交替计算每个柱子

相当于算法2的进一步优化,舍去了储存两端最高高度的数组,只记录当下遍历过的左右端最高高度,之所以能这么计算,是当我们判断左边最高高度高于右边时,这时我们就能算出右边当下端点的储水量,值为右边最大高度-右边当下端点高度,然后继续移动右边端点,交替计算另一方的储水量,这样就不用把全部端点左右最大高度存储起来,以节省存储空间。

class Solution {
public:
    int trap(vector<int>& height) {
        int n =height.size();
        int left = 0,right = n-1;
        int ans = 0;
        int l_max = height[0];
        int r_max = height[n-1];
        while(left<=right){
            l_max = max(l_max,height[left]);
            r_max = max(r_max,height[right]);

            if(l_max<r_max){
                ans += l_max - height[left];
                left++;
            }
            else{
                ans += r_max - height[right];
                right--;
            }
        }
        return ans;
    }
};
双指针堆积木计算总体积(高度上升,把上升的那层体积加上)
class Solution {
public:
    int trap(vector<int>& height) {
        int n =height.size();
        int high = 0; int buff = 0;int volume = 0;int solid = 0;

        for(int i=0,j=n-1;i<=j;){
            if(min(height[i],height[j])>high){
                buff = min(height[i],height[j]);//暂存当前蓄水高度,因为后面还要用到之前高度做差
                volume = (buff-high)*(j-i+1)+volume;//计算总体积
                high = buff;
            }
            if(height[i]<=height[j]) i++;
            else j--;
        }

        for(int i =0;i<n;i++)
            solid = solid +height[i];//计算柱子体积
        return volume-solid;
    }
};

二. 接雨水II(小根堆+贪心)

从外圈开始,贪心选择短板,计算内部相邻柱子,计算储水量,并进行短板替换,使圈子内缩

typedef pair<int,int> pii;

class Solution {
public:
    int trapRainWater(vector<vector<int>>& heightMap) {  
        int m = heightMap.size();
        int n = heightMap[0].size();
        if(m<=2||n<=2) return 0;

        priority_queue<pii, vector<pii>, greater<pii>> pq;//小根堆(按高度排)存储高度和坐标
        vector<vector<bool>> visit(m, vector<bool>(n, false));

        for (int i = 0; i < m; i++) //最外圈边界入堆
            for (int j = 0; j < n; j++) 
                if (i == 0 || i == m - 1 || j == 0 || j == n - 1){
                    pq.push({heightMap[i][j], i * n + j});//坐标一维化
                    visit[i][j] = true;//标记访问过的
                }

        int res = 0;
        int dirs[] = {-1, 0, 1, 0, -1};//顺时针
        while (!pq.empty()) {//遍历到堆为空
            pii cur = pq.top();//木桶短板出堆
            pq.pop();            
            for (int k = 0; k < 4; k++) {//顺时针查看周围坐标
                int nx = cur.second / n + dirs[k];//转回二维
                int ny = cur.second % n + dirs[k + 1];//转回二维
                if( nx >= 0 && nx < m && ny >= 0 && ny < n && !visit[nx][ny]) {//限制条件
                    if (heightMap[nx][ny] < cur.first)//当前高度小于最小高度
                        res += cur.first - heightMap[nx][ny]; //计算当前柱子蓄水量
                    visit[nx][ny] = true;//标记防止重复
                    //关键语句,把更大的值放回堆中(短板只会增大,不会变小)
                    //把新坐标放回堆中,相当于外圈往内缩
                    pq.push({max(heightMap[nx][ny], cur.first), nx * n + ny});
                }
            }
        }
        return res;
    }
};
posted @ 2022-05-10 23:39  失控D大白兔  阅读(55)  评论(0编辑  收藏  举报