当青训营遇上码上掘金
题目背景
现有 n 个宽度为 1 的柱子,给出 n 个非负整数依次表示柱子的高度,排列后如下图所示,此时均匀从上空向下撒青豆,计算按此排列的柱子能接住多少青豆。(不考虑边角堆积)
以下为上图例子的解析:
输入:height = [5,0,2,1,4,0,1,0,3]
输出:17
解析:上面是由数组 [5,0,2,1,4,0,1,0,3] 表示的柱子高度,在这种情况下,可以接 17 个单位的青豆。
这一题其实和接雨水的方法非常的类似,都是面试中非常常见的题目。 接雨水:leetcode.cn/problems/tr…
解题方法
大致有三种解题方法
- 双指针法
- 动态规划
- 单调栈 下面以双指针法进行详细讲解,这也是最常见的解题方法。
双指针法
双指针法有两个思路:要么是按行来计算,要么是按列来计算。两种方法解题思路类似,但是推荐选取一种来实现,不要交叉使用 下面以按列计算为示例: 以列来计算,那么每一列青豆的宽度是1,我们只需要计算青豆的高度就可以了。而青豆的高度取决于,该列左侧最高柱子和右侧最高柱子中较矮的那个柱子高度。
就拿图中的样式举例,一共有8列,列名为0~7 列2左侧最高的柱子是列0,高度为5(用LHeight表示) 列2右侧最高的柱子是列4,高度为4(用RHeight表示) 列2的柱子的高度为2(用Height表示) 那么列2的青豆高度就是列0和列4高度的较小值减列2的高度,即 min(LHeight, RHeight) - height 列2的青豆高度就求出来了,宽度是1,与高度相乘就是青豆的体积了。
通过遍历的方法,可以从头开始遍历一遍所有列,然后求出每一列的青豆的体积,相加之后就是青豆的总体积了。 但是要注意的是边界情况:第一个柱子和最后一个柱子是不算青豆的:代码如下:
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; // 注意只有h大于零的时候,在统计到总和中
该算法的时间复杂度是O(n^2),空间复杂度是O(1)