攒青豆的三种思考

57 阅读2分钟

当青训营遇上码上掘金

题目

现有 n 个宽度为 1 的柱子,给出 n 个非负整数依次表示柱子的高度,排列后如下图所示,此时均匀从上空向下撒青豆,计算按此排列的柱子能接住多少青豆。(不考虑边角堆积)

725ef710a59944d49d0315bece7a1ac1~tplv-k3u1fbpfcp-zoom-in-crop-mark-3024-0-0-0.image.png 以下为上图例子的解析: 输入:height = [5,0,2,1,4,0,1,0,3] 输出:17 解析:上面是由数组 [5,0,2,1,4,0,1,0,3] 表示的柱子高度,在这种情况下,可以接 17 个单位的青豆。

思路分析

     刚开始看到这个题,是没什么思路,在网上学习后,发现可以用分行法,动态规划法和双指针法三种方法去解析。第一个时间复杂度较高,后两者时间复杂度相同O(n) ,但是双指针法的空间复杂度很低O(1)

分行法

     此方法的基本思想是按行去遍历,从高度为1开始,一直到数组中最大数的高度,一行一行的去计算。比方说求第i层,遍历所有元素,如果>=i则舍去;如果<i则保留。但是需要注意的是要用一个temp变量去存储每一行中每一小段雨水,还有通过一个flag变量判断是否开始(因为题目要求不考虑边角堆积)。

代码如下:

#include<iostream>
#include<vector>
using namespace std;
int main(){
    vector<int> height {5,0,2,1,4,0,1,0,3};
    int max = *max_element(height.begin(),height.end());
    int ans = 0;
    int temp = 0;
    int flag = -1;
    for(int i=1;i<=max;i++){
        temp = 0;
        flag = -1;
        for(int j=0;j<height.size();j++){
            if (height[j] < i && flag == 1){
                temp++;
            }else if(height[j] >= i){
                flag = 1;
                ans+=temp;
                temp = 0;
            }
        }
    }
    cout<<"Answer is : "<<ans<<endl;
    return 0;
}

动态规划法

     此方法的思想是分别建立两个数组leftright,分别从左和右开始遍历,对数据进行预处理,然后再通过两个数组的值进行组合求解。

代码如下:

#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
int main(){
     vector<int> height {5,0,2,1,4,0,1,0,3};
     int length = height.size();
     vector<int> left; vector<int> right; int sum = 0;
     left.push_back(height[0]);
     right.push_back(height[length-1]);
     for(int i=1;i<length;i++){   
         left.push_back(max(left[i-1],height[i]));
     }
     for(int i=length-2;i>=0;i--){
         right.insert(right.begin(),max(height[i],right[0]));
     }
     for(int i=0;i<length;i++){
         sum += (min(left[i],right[i])-height[i]);
     }
     cout<<"Answer is = "<<sum<<endl;
     return 0;
     }

双指针法

     此方法的思想是基于动态规划之上,动态规划是分别建立两个数组从两端进行遍历,而双指针法则是通过leftright两个指针从两端同时开始遍历,进行累加。

代码如下:

#include<iostream>
#include<vector>
using namespace std;
int main(){
    vector<int> height {5,0,2,1,4,0,1,0,3};
    int left=0,leftMax=height[0],right=height.size()-1,rightMax=height[right];
    int sum = 0;
    while(left<right){
        if(height[left]>leftMax){
            leftMax = height[left];
        }
        if(height[right]>rightMax){
            rightMax = height[right];
        }
        if(height[left]<height[right]){
            sum+=leftMax-height[left];
            left++;
        }else{
            sum+=rightMax-height[right];
            right--;
        }
    }
    cout<<"Answer is = "<<sum<<endl;
    return 0;
}

顺便附上我的码上掘金链接 攒青豆项目代码