当青训营遇上码上掘金 主题四:攒青豆

81 阅读1分钟

当青训营遇上码上掘金

题目

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

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

思路

看完题目后,一眼接雨水

这道题目可用单调栈解决:

单调栈:

单调栈是一种理解起来很容易,但是运用起来并不那么简单的数据结构。一句话解释单调栈,就是一个栈,里面的元素的大小按照他们所在栈内的位置,满足一定的单调性。

思路

栈头元素:凹槽底部的柱子,栈头的第二个元素:凹槽左边的柱子,添加的元素:凹槽右边的柱子

从栈头到栈底应该是从小到大的顺序,要添加的元素(右边的柱子)发现大于栈顶元素(凹槽底部)了

我们分三种情况:

  • 当遍历到的元素小于栈顶元素即height[i] < height[st.top()] 时,入栈

    if(height[i] < height[st.top()]) {      st.push(i);}
    
  • 当遍历到的元素等于栈顶元素时,将栈顶元素出栈并入栈顶元素

    if (height[i] == height[st.top()]) {      st.pop();      st.push(i);}
    
  • 当遍历到的元素大于栈顶元素时,出栈顶元素直到栈顶元素大于等于该元素,然后进行计算:高取左边界和右边界的最小值减去凹槽底部,底取i - st.top() - 1

     while(!st.empty() && height[st.top()] < height[i]) {        int mid = st.top(); st.pop();        if(!st.empty()) {          int h = min(height[st.top()], height[i]) - height[mid];          int r = i - st.top() - 1;          sum += h * r;        }      }st.push(i); // 最后别忘记该元素还要做凹槽的左边
    

遍历完成后,sum即为最后答案

举例:(为了方便,上面的数字表示索引对应的高度)

  • 当遍历到元素2时(2 > 0):
  • 当遍历到元素4时 (4 > 1):

当计算完上面两小块后,栈内还剩元素5、2(同样为了方便表示),再继续计算4左边的剩余青豆数,计算完成后,左半边计算完毕

代码(C++)

#include<iostream>#include<vector>#include <stack>using namespace std;int trap(vector<int> &height) {  if(height.size() <= 2) {    return 0;  }  stack<int> st;  st.push(0);  int sum = 0;  for(int i = 1; i < height.size(); i++) {    if(height[i] < height[st.top()]) {      st.push(i);    }else if (height[i] == height[st.top()]) {      st.pop();      st.push(i);    }else {      while(!st.empty() && height[st.top()] < height[i]) {        int mid = st.top(); st.pop();        if(!st.empty()) {          int h = min(height[st.top()], height[i]) - height[mid];          int r = i - st.top() - 1;          sum += h * r;        }      }      st.push(i);    }  }  return sum;}int main() {  vector<int> height;    int x;  while (cin >> x) {    height.push_back(x);    if (cin.get() == '\n')  break;  }  cout << endl;  cout << trap(height) << endl;  return 0;}