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

75 阅读2分钟

当青训营遇上码上掘金

题目描述

现有 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 的长度。从 0n-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
}

码上掘金Code