面试题 17.21. 直方图的水量
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/volume-of-histogram-lcci
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
题目
给定一个直方图(也称柱状图),假设有人从上面源源不断地倒水,最后直方图能存多少水量?直方图的宽度为 1。
上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的直方图,在这种情况下,可以接 6 个单位的水(蓝色部分表示水)。 感谢 Marcos 贡献此图。
示例:
输入: [0,1,0,2,1,0,1,3,2,1,2,1]
输出: 6
思路
这题也是用的动态规划的思路吧,首先想一下中间的某一个柱子,先找到左边最高的柱子,再找到右边最高的柱子,如果左右的柱子都比中间的那个柱子要高的话,那这个柱子上面肯定能接水,水的高度就是左右最高的柱子中相对矮点的那个减去中间柱子的高度。
剩下的就是这么找到左右最高的柱子了,利用两个指针去存一下就行了。上代码吧。
其他也有用栈的做法,似乎也可以二分递归的思路,方法不一,看自己擅长哪种了。
代码
详细思路:
public static int trap(int[] height) {
if(height == null || height.length <= 1)
return 0;
int res = 0;
// 先存下数组第一个值的高度,当成左边最大值
int lm = height[0];
int rm = 0;
int rindex = 0;
// 从数组第二个开始循环,因为数组第一个无论如何都接不了水
for (int i = 1; i < height.length; i++) {
// 如果当前位置超出了右边最大值的索引
if (i > rindex) {
// 重新初始化右边柱子最大值
rm = 0;
// 找到右边柱子最大值,记下最大值的索引
for (int j = i; j < height.length; j++) {
if (rm < height[j]) {
rm = height[j];
rindex = j;
}
}
}
// 如果左边的最大值的高度、右边的最大值的高度都比当前位置高的话
if (rm > height[i] && lm > height[i]) {
// 左右两边矮一点的那个减去当前的高度就是当前位置能接的雨水格数
int x = Math.min(rm, lm) - height[i];
// 加上雨水格数
res += x;
}
// 如果当前位置比左边最高的柱子还高一些,就更新左边最大值,进入下一次循环
if (lm < height[i]) {
lm = height[i];
}
}
return res;
}
简化版的代码:
public static int trap(int[] height) {
if(height == null || height.length <= 1)
return 0;
int res = 0;
int left = 0;
int right = 0;
for (int i = 1; i < height.length; i++) {
// 如果超过了右边最大值的索引,重新找右边新的最大值
if (i > right) {
right = i;
for (int j = i; j < height.length; j++) {
if (height[right] < height[j]) {
right = j;
}
}
}
// 如果左边的最大值的高度、右边的最大值的高度都比当前位置高的话
if (height[left] > height[i] && height[right] > height[i]) {
// 左右两边矮一点的那个减去当前的高度就是当前位置能接的雨水格数,加上即可
res += Math.min(height[left], height[right]) - height[i];
}
// 更新左边最大值,进入下一次循环
if (height[left] < height[i]) {
left = i;
}
}
return res;
}