LeetCode42.接雨水(暴力、DP、单调栈)

42. 接雨水

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

示例1.

输入:height = [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 ] [0,1,0,2,1,0,1,3,2,1,2,1] [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图,在这种情况下,可以接 6 6 6个单位的雨水(蓝色部分表示雨水);

示例 2.

输入:height = [4,2,0,3,2,5]
输出:9

思路:

  • 暴力方法:按照列的方式对每列所能接到的雨水量进行计算,对于每列而言,找到其左右两边的最高的柱子,取左右最高柱子中的小值,减去该列的柱子高度即是该列柱子所能接水量。对于每个柱子而言,寻找其左右最高柱子需要额外花费 O ( n ) O(n) O(n)时间,对于 n n n个柱子而言,总体的时间复杂度为 O ( n 2 ) O(n^2) O(n2),而其中未使用辅助空间,则空间复杂度为 O ( 1 ) O(1) O(1)
    int trap(vector<int>& height) {
    
    
        //按照列计算的暴力方法,时间复杂度O(n^2),空间复杂度O(1)
        //对于每列而言,找到其左右两边的最高的柱子,取左右最高柱子中的小值,减去该列的柱子高度
        //即为该列的接水多少
        int res = 0;
        for(int i = 0; i < height.size(); ++i) {
    
    
            if(i == 0 || i == height.size()-1) continue;
            int rHeight = height[i], lHeight = height[i];
            for(int rIdx = i+1; rIdx < height.size(); ++rIdx) //寻找右边最高的柱子
                if(height[rIdx] > rHeight) rHeight = height[rIdx];
            for(int lIdx = i-1; lIdx >= 0; --lIdx) //寻找左边最高的柱子
                if(height[lIdx] > lHeight) lHeight = height[lIdx];
            res += max(min(lHeight, rHeight) - height[i], 0); //若是接水量大于0,加上该水量
        }
        return res;
    }
  • 动态规划方法:对于暴力方法,可以看到每次寻找当前柱子的左右最高柱子的过程中有许多的重复操作,则可开辟两个相同大小的辅助空间 m a x L e f t maxLeft maxLeft, m a x R i g h t maxRight maxRight分别记录每个柱子 h e i g h t [ i ] height[i] height[i]的左最高柱子 m a x L e f t [ i ] maxLeft[i] maxLeft[i]和右最高柱子高度 m a x R i g h t [ i ] maxRight[i] maxRight[i]。对于左最高柱子,从左向右进行记录,有 m a x L e f t [ i ] = m a x ( h e i g h t [ i ] , m a x L e f t [ i − 1 ] ) maxLeft[i]=max(height[i], maxLeft[i-1]) maxLeft[i]=max(height[i],maxLeft[i1]);对于右最高柱子,从右向左进行记录,有 m a x R i g h t [ i ] = m a x ( h e i g h t [ i ] , m a x R i g h t [ i − 1 ] ) maxRight[i]=max(height[i], maxRight[i-1]) maxRight[i]=max(height[i],maxRight[i1])。有了左右最高柱的高度值,计算总接水量和暴力方法类同。总体的时间复杂度为 O ( n ) O(n) O(n),其中使用辅助空间,则空间复杂度为 O ( n ) O(n) O(n)
    int trap(vector<int>& height) {
    
    
        if(height.size() < 2) return 0;
        //dp算法
        //从左向右遍历:maxLeft[i] = max(height[i], maxLeft[i - 1]);
        //从右向左遍历:maxRight[i] = max(height[i], maxRight[i + 1]);
        int res = 0;
        vector<int> maxLeft(height.size(), 0);
        vector<int> maxRight(height.size(), 0);

        // 记录每个柱子左边柱子最大高度
        maxLeft[0] = height[0];
        for(int i = 1; i < maxLeft.size(); ++i) 
            maxLeft[i] = max(height[i], maxLeft[i-1]);

        // 记录每个柱子右边柱子最大高度
        maxRight[maxRight.size()-1] = height[height.size()-1];
        for(int i = maxRight.size()-2; i >= 0; --i)
            maxRight[i] = max(height[i], maxRight[i+1]);

        for(int i = 0; i < height.size(); ++i)
            res += max(min(maxLeft[i], maxRight[i]) - height[i], 0);
        return res;
    }
  • 单调栈 (类似题目)
    使用的单调递减栈,栈顶至栈底是单调递增的,即栈底到栈顶是从大到小的。
    若是当前柱子高度小于栈顶柱子高度,则将当前柱子的列数压入栈中;
    若是当前柱子高度等于栈顶柱子高度,则将栈顶柱子弹出,将当前柱子压入;
    若是当前柱子高度大于栈顶柱子高度,则出现凹槽,依次与栈顶元素进行对照,计算凹槽的面积并累加,直至当前柱高不大于栈顶柱高,或是栈为空,再将当前柱子压入。
    按照如上方法,直至将所有的柱子遍历一遍即可。
    总体的时间复杂度为 O ( n ) O(n) O(n),其中使用辅助空间,则空间复杂度为 O ( n ) O(n) O(n)
    其是按行计算的。如下图所示:
    int trap(vector<int>& height) {
    
    
        //使用单调栈方法,栈顶至栈底是单调递增的,即栈底到栈顶是从大到小的
        //若是当前柱子高度小于栈顶柱子高度,则将当前柱子的列数压入栈中;
        //若是当前柱子高度等于栈顶柱子高度,则将栈顶柱子弹出,将当前柱子压入;
        //若是当前柱子高度大于栈顶柱子高度,则出现凹槽,依次与栈顶元素进行对照,计算凹槽的面积并累加,
        //直至当前柱高不大于栈顶柱高,或是栈为空,再将当前柱子压入
        stack<int> idxRrd;
        idxRrd.push(0);
        int res = 0;
        for(int i = 1; i < height.size(); ++i) {
    
    
            if(height[i] < height[idxRrd.top()]) {
    
     //当前柱子高度小于栈顶所对应的高度
                idxRrd.push(i);
            } else if(height[i] == height[idxRrd.top()]) {
    
     //当前柱子高度等于栈顶所对应的高度
                idxRrd.pop();
                idxRrd.push(i);
            } else {
    
     //当前柱子高度大于栈顶所对应的高度,此时出现凹槽
                while(!idxRrd.empty() && height[i] > height[idxRrd.top()]) {
    
    
                    int caveIdx = idxRrd.top();
                    idxRrd.pop();
                    if(!idxRrd.empty()) {
    
    
                        int h = min(height[idxRrd.top()], height[i]) - height[caveIdx];
                        int w = i - idxRrd.top() - 1;
                        res += h*w;
                    }
                }
                idxRrd.push(i);
            }
        }
        return res;
    }
  • 其他方法:三个指针,左右指针不相遇时继续循环,同时将左或右指针进行移位,总体时间复杂度 O ( n ) O(n) O(n),空间复杂度 O ( 1 ) O(1) O(1)
    int trap(vector<int>& height) {
    
    
        if(height.size() < 3) return 0;
        int left = 0, right = height.size()-1;
        int res = 0;
        while(left < right) {
    
    
            if(height[right] >= height[left]) {
    
    
                int slide = left + 1;
                while(height[left] > height[slide]) {
    
    
                    res += height[left] - height[slide++];
                }
                left = slide;
            } else {
    
    
                int slide = right - 1;
                while(height[right] > height[slide]) {
    
    
                    res += height[right] - height[slide--];
                }
                right = slide;
            }
        }
        return res;
    }

猜你喜欢

转载自blog.csdn.net/yueguangmuyu/article/details/112180954
今日推荐