攒青豆 |「青训营 X 码上掘金」主题创作

33 阅读2分钟

当青训营遇上码上掘金

主题4——攒青豆

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

image.png

以下为上图例子的解析:

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

输出:17

解析:上面是由数组 [5,0,2,1,4,0,1,0,3] 表示的柱子高度,在这种情况下,可以接 17 个单位的青豆

此题与leetcode接雨水相似,有多种解法

1.暴力法

对于每个位置,分别向左和向右扫描并记录左边和右边的最大高度,然后计算每个下标位置能接的青豆量,比如对于位置i,向i-1至1位置(起始位1),与i+1至n位置遍历,找出左右最大高度并与此位置高度相比,得出次位置能攒的青豆量

此方法时间复杂度O(n2)O(n^2),空间复杂度为O(n)O(n)

2.单调栈法

使用单调栈记录各位置高度,同时满足小顶(指从栈顶至栈底递增),这样对于栈顶元素i与其下元素j(满足i>j),一定有height[i]<height[j],这样当新的位置入栈时,若其高度大于栈顶,则可计算此位置空间内能攒的青豆数量

代码:

#include<iostream>
#include<vector>
#include<stack>
using namespace std;
int solution(vector<int>& height) {
        int ans = 0;
        stack<int> stk; //单调栈
        int n = height.size();
        for (int i = 0; i < n; ++i) {
            while (!stk.empty() && height[i] > height[stk.top()]) {
                int top = stk.top();//当栈顶元素小于入栈元素时,出栈,直至重新形成小顶
                stk.pop();
                if (stk.empty()) {
                    break;
                }
                int left = stk.top();
                int currWidth = i - left - 1;//计算此空间内能存储的青豆量
                int currHeight = min(height[left], height[i]) - height[top];
                ans += currWidth * currHeight;
            }
            stk.push(i);
        }
        return ans;
}

此算法时间复杂度 O(n)O(n),空间复杂度 O(n)O(n)

3.双指针法

用左右两个指针记录左方最大值leftmax和右方最大值rightmax,左指针只能右移,右指针只能左移,同时计算每个位置的容量,每次移动指针一般移动最大值小的那个

如:leftmax<rightmax,则此位置容量为Leftmax-height[i],同时向右移动左指针,

以此类推,直至两指针相遇