42. 接雨水

在这里插入图片描述
方法一:栈

需要画图体验。
当前高度不大于栈顶入栈;
当前高度大于栈顶时,栈顶出栈,此时出栈的值最小,相当于桶底。比较当前高度和新的栈顶高度,它们相当于桶壁,较小值和出栈值的差为盛水高度。
需要注意的是,如果出栈后栈空则直接返回,因为一个桶壁是不能盛水的;并且需要考虑两个桶壁的距离。

class Solution {
public:
    int trap(vector<int>& height) {
        int res=0;
        stack<int> st;
        int i=0;    //i为数组下标
        while(i<height.size()){
            while(!st.empty() && height[i]>height[st.top()]){//如果栈不为空,并且当前高度大于栈顶高度
                int n=height[st.top()];	//保存栈顶高度,并出栈
                st.pop();
                if(st.empty())	//如果此时栈空直接返回
                    break;
                int distance=i-st.top()-1;	//计算当前下标和弹出栈顶下标的距离
                res+=distance * (min(height[st.top()],height[i]) - n);	//计算盛水量
            }
            st.push(i++);	//如果栈空或者当前高度小于栈顶高度,入栈,下标后移
        }
        return res;
    }
};

方法二:双指针

class Solution {
public:
    int trap(vector<int>& height) {
        if(height.size()<2) return 0;
        int res=0;
        int maxleft=0,maxright=0;   //左右边最大高度
        int left=1,right=height.size()-2;   //左右指针
        for(int i=1;i<height.size()-1;i++){
            //从左到右更新
            if(height[left-1]<height[right+1]){
                maxleft=max(maxleft,height[left-1]);
                if(maxleft>height[left])
                    res+= maxleft-height[left];
                ++left;
            }
            //从右到左更新
            else{
                maxright=max(maxright,height[right+1]);
                if(maxright>height[right])
                    res+= maxright-height[right];
                --right;
            }
        }
        return res;
    }
};

方法三:按行计算

每行计算,在第n行时,如果当前高度小于n并且有两边高度大于等于n的,说明这个地方有水。用一个tmp变量保存当前层累积量,当第一次出现大于等于n的高度时,tmp开始更新:如果遇到小于n的数,tmp加1;如果遇到大于等于n的数,则tmp加入到总数res中并将tmp清零。
这种方法会超时!

class Solution {
public:
    int trap(vector<int>& height) {
        int max=0;
        for(int i=0;i<height.size();i++){    //求出最高行
            if(height[i]>max)
                max=height[i]; 
        }
        int res=0;
        for(int i=1;i<=max;i++){
            bool first=true;    //用于判断是否更新tmp
            int tmp=0;
            for(int j=0;j<height.size();j++){
                if(height[j]<i && first)    //没出现
                    continue;
                first=false;    //第一次出现
                if(height[j]<i) //开始更新
                    ++tmp;
                else{
                    res+=tmp;
                    tmp=0;
                }
            }
            first=true;
            tmp=0;
        }
        return res;
    }
};

方法四:按列求

两边不用考虑,判断当前高度与左右两边最大高度的较小值的大小,如果小于较小值则差值为当前列的盛水量。
时间复杂度:O(n²)
空间复杂度:O(1)

class Solution {
public:
    int trap(vector<int>& height) {
        if(height.size()<2)  return 0;
        int res=0;
        for(int i=1;i<height.size()-1;i++){ //左右两边都不用考虑,不会有水
            int max_left=0; //左边最大值
            for(int j=0;j<i;j++){
                if(height[j]>max_left)
                    max_left=height[j];
            }
            int max_right=0;    //右边最大值
            for(int j=i+1;j<height.size();j++){
                if(height[j]>max_right)
                    max_right=height[j];
            }
            if(height[i]<min(max_left,max_right))   //如果当前高度小于两边最大中较小的
                res+=min(max_left,max_right)-height[i]; //差值为盛水量
        }
        return res;
    }
};

方法五:动态规划

优化方法四,用两个数组来保存左边最大高度和右边最大高度。
时间复杂度:O(n)
空间复杂度:O(n)

class Solution {
public:
    int trap(vector<int>& height) {
        if(height.size()<2)  return 0;
        int res=0;
        int max_left[height.size()]={0};    //表示左边最大值
        int max_right[height.size()]={0};   //表示右边最大值
        for(int i=1;i<=height.size()-1;i++)    //i从第二个开始
            max_left[i]=max(max_left[i-1],height[i-1]); 
        for(int i=height.size()-2;i>=0;i--)   //i从倒数第二个开始
            max_right[i]=max(max_right[i+1],height[i+1]);
        for(int i=1;i<height.size()-1;i++){  //左右两端都不用考虑,不会有水       
            if(height[i]<min(max_left[i],max_right[i]))  //如果当前高度小于两边最大中较小的
                res+=min(max_left[i],max_right[i])-height[i]; //差值为盛水量
        }
        return res;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值