「青训营 X 码上掘金」主题 4——攒青豆

44 阅读1分钟

当青训营遇上码上掘金

主题介绍

现有 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 个单位的青豆。

思路解析

本题有两种解法:

  • 双指针法
  • 单调栈法

双指针法

本题按照列来计算,计算每一列的数目,宽度为1,高度为左边最高值和右边最高值中最小值减去本列的高度,就是所求的青豆体积。 以上图高度为2的柱子所示,左侧最高为5,右侧最高为4,二者最小值为4,所以本列的高度为 4-2=2,宽度为1,所以体积为2,遍历整个数组,将每列的体积加起来,就是结果。

注意:第一个和最后一个柱子不接青豆

代码如下:

#include<iostream>
#include<vector>
using namespace std;
class Solution {
public:
    int trap(vector<int>& height) {
        int sum = 0;
        for (int i = 0; i < height.size(); i++) {
            if (i == 0 || i == height.size() - 1) continue; //跳过第一个和最后一个柱子

            int rHeight = height[i]; // 记录右边柱子的最高高度
            int lHeight = height[i]; // 记录左边柱子的最高高度
            for (int r = i + 1; r < height.size(); r++) {
                if (height[r] > rHeight) rHeight = height[r];
            }
            for (int l = i - 1; l >= 0; l--) {
                if (height[l] > lHeight) lHeight = height[l];
            }
            int h = min(lHeight, rHeight) - height[i];
            if (h > 0) sum += h;
        }
        return sum;
    }
};
int main() {
  Solution s;
  vector<int> height{5,0,2,1,4,0,1,0,3};
  int result = s.trap(height);
  cout<< result << endl;
  return 0;
}

因为每次遍历列的时候,还要向两边寻找最高的列,所以时间复杂度为O(n^2)。 空间复杂度为O(1)。

单调栈法

此方法是按照行来计算的。

从栈头(元素从栈头弹出)到栈底的顺序应该是从小到大的顺序。 因为一旦发现添加的柱子高度大于栈头元素了,此时就出现凹槽了,栈头元素就是凹槽底部的柱子,栈头第二个元素就是凹槽左边的柱子,而添加的元素就是凹槽右边的柱子。 是用单调栈,其实是通过 长 * 宽 来计算青豆体积的。 长就是通过柱子的高度来计算,宽是通过柱子之间的下标来计算

处理逻辑

先将下标0的柱子加入到栈中,st.push(0);

然后开始从下标1开始遍历所有的柱子,for (int i = 1; i < height.size(); i++)

如果当前遍历的元素(柱子)高度小于栈顶元素的高度,就把这个元素加入栈中,因为栈里本来就要保持从小到大的顺序(从栈头到栈底)。

如果当前遍历的元素(柱子)高度等于栈顶元素的高度,要跟更新栈顶元素,因为遇到相相同高度的柱子,需要使用最右边的柱子来计算宽度。 如果当前遍历的元素(柱子)高度大于栈顶元素的高度,此时就出现凹槽了,

代码如下:

class Solution {
public:
    int trap(vector<int>& height) {
        stack<int> st;
        st.push(0);
        int sum = 0;
        for (int i = 1; i < height.size(); i++) {
            while (!st.empty() && height[i] > height[st.top()]) {
                int mid = st.top();
                st.pop();
                if (!st.empty()) {
                    int h = min(height[st.top()], height[i]) - height[mid];
                    int w = i - st.top() - 1;
                    sum += h * w;
                }
            }
            st.push(i);
        }
        return sum;
    }
};

码上掘金发布链接: 攒青豆 - 码上掘金 (juejin.cn)