当青训营遇上码上掘金
题目描述
现有 n 个宽度为 11 的柱子,给出 n 个非负整数依次表示柱子的高度,排列后如下图所示,此时均匀从上空向下撒青豆,计算按此排列的柱子能接住多少青豆。(不考虑边角堆积)
以下为上图例子的解析: 输入:height = [5,0,2,1,4,0,1,0,3] 输出:17 解析:上面是由数组 [5,0,2,1,4,0,1,0,3] 表示的柱子高度,在这种情况下,可以接 17 个单位的青豆。
解题思路
这道题是一道经典的单调栈题目,当然也可以用其他算法,如动态规划、双指针来解答。下面来讲解下单调栈做法的思路。
首先,我们依次遍历柱子高度,以左为边界,计算右边柱子对其产生的影响。较高柱子会对右边更多的柱子产生影响,因此,我们使用数据结构栈来存储,堆成一个金字塔形。当我们判断一个柱子时,先查看堆栈是否为空,若不为空,判断柱子高度和栈顶柱子高度谁更高,若栈顶柱子高度更高,就把当前柱子加入栈顶,否则计算堆栈中与其最近的柱子和他所能容纳的豆子,并标记下当前豆子填补到的高度,循环进行这一步。
算法复杂度
时间复杂度:O(n),其中 n 是数组 height 的长度。从 0 到 n-1 的每个下标最多只会入栈和出栈各一次。
空间复杂度:O(n),其中 n 是数组 height 的长度。空间复杂度主要取决于栈空间,栈的大小不会超过 n。
代码实现
C++
class Solution {
public:
int trap(vector<int>& height) {
int n = height.size();
int res = 0;
stack<int> st;
for (int i = 0; i < n; i++) {
int last = 0;
while (!st.empty() && height[i] > height[st.top()]) {
res += (height[st.top()] - last) * (i - st.top() - 1);
last = height[st.top()];
st.pop();
}
if (!st.empty()) {
res += (height[i] - last) * (i - st.top() - 1);
}
st.push(i);
}
return res;
}
};
go
func trap(height []int) (res int) {
st := []int{}
for i, h := range height {
last := 0
for len(st) > 0 && h > height[st[len(st) - 1]] {
top := st[len(st) - 1]
res += (height[top] - last) * (i - top - 1)
last = height[top];
st = st[:len(st) - 1]
}
if len(st) > 0 {
res += (height[i] - last) * (i - st[len(st) - 1] - 1)
}
st = append(st, i)
}
return
}