leetcode 42. 接雨水

42. 接雨水

问题描述

给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。
上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图,在这种情况下,可以接 6 个单位的雨水(蓝色部分表示雨水)。 
示例:
输入: [0,1,0,2,1,0,1,3,2,1,2,1]
输出: 6

问题分析

解法一:我们从局部考虑,一个格子可以接的雨水数量和这个格子左右两端的高度的最小值有关,他能接的雨水的数量为高度的最小值减去这个格子的高度,因此可以使用暴力求解,代码如下:

class Solution {
public:
    int trap(vector<int>& height) {
        int n = height.size();
        int i,j,r_max,l_max,tmp;
        int ans = 0;
        for(i = 0; i < n; i++)
        {
            r_max = 0;l_max=0;
            //j从0开始扫描,因此是找左端最大值
            for(j = 0; j <i; j++)
            {
                l_max = max(l_max,height[j]);
            }
            //j从n-1开始扫描,因此是找右端最大值
            for(j = n-1; j >i; j--)
            {
                r_max = max(r_max,height[j]);
            }
            tmp = min(l_max,r_max)-height[i];
            if(tmp > 0)
                ans += tmp;
        }
        return ans;
    }
};

该方法的复杂度为O(N^2),求解时间也不甚理想:

执行用时 :492 ms, 在所有 cpp 提交中击败了5.05%的用户
内存消耗 :8.9 MB, 在所有 cpp 提交中击败了95.54%的用户

下面考虑解法二:解法一复杂度高是因为每一步都要遍历一遍寻找左右两端最大的高度,我们可以把这个高度存下来,利用好历史信息,用空间来换时间,即定义数组r_max[i]代表第i个位置格子右端最大的高度,l_max[i]代表第i个位置格子左端最大的高度,则r_max[i+1]=max(r_max[i+1],height[i]),l_max[i+1]=max(l_max[i],height[i+1]),因此代码如下:

class Solution {
public:
    int trap(vector<int>& height) {
        int n = height.size();
        if(n == 0) return 0;
        vector<int> r_max(n,0),l_max(n,0);
        int i,ans = 0,tmp;
        l_max[0] = height[0];
        r_max[n-1] = height[n-1];//注意不要写为r_max[0] = height[n-1];
        for(i = 1; i < n; i++)
            l_max[i] = max(l_max[i-1],height[i]);
        for(i = n-2; i>=0; i--)
            r_max[i] = max(r_max[i+1],height[i]);
        //因为第0个与最后一个肯定没法接雨水,所以可以不用计算
        for(i = 1; i < n-1; i++)
        {
            tmp = min(l_max[i],r_max[i]) - height[i];
            if(tmp > 0)
                ans += tmp;
        }
        return ans;
    }
};

此时的结果为

执行用时 :8 ms, 在所有 cpp 提交中击败了79.00%的用户
内存消耗 :9.2 MB, 在所有 cpp 提交中击败了74.84%的用户

解法三:我们希望使用更少的存储空间来解决这个问题,这时需要用到双指针left与right,其中l_max代表height[0...left]的最大高度,r_max代表height[right...n-1]的最大高度,显然left遍历到位置i,l_max一定是左端最大的高度,但是r_max不一定是右端最大的高度,但是如果l_max<=r_max,则l_max一定比右端最大高度小,此时盛水量完全由l_max决定,如果right遍历到位置i,r_max一定是右端最大高度,但是l_max不一定是左端最大高度,但是如果r_max<=l_max,r_max一定比左端最大高度小,此时盛水量完全由r_max决定,下面给出代码:

class Solution {
public:
    int trap(vector<int>& height) {
        int n = height.size();
        if(n == 0) return 0;
        int right=n-1,left=0,l_max=0,r_max=0;
        int ans = 0;
        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;
    }
};

此时的结果为

执行用时 :8 ms, 在所有 cpp 提交中击败了79.00%的用户
内存消耗 :9 MB, 在所有 cpp 提交中击败了90.02%的用户
posted @ 2019-11-10 19:48  曲径通霄  阅读(383)  评论(0编辑  收藏  举报