引言
力扣题目【直方图的水量】描述 -> 传送门
所用语言:Java
执行用时 1 ms,内存消耗 38.1 MB
正文
昨天和前天的题解由于时间比较匆忙,没有来得及写的很详细 😣(不过也没有多少人看就是了)
今天是假期的第一天,时间比较充裕,我尽量将本篇题解写的详细一点,也对得起这道困难题。
我该怎么和你说清楚我的思路呢?🤔
👇这些是废话,要是时间紧迫,可直接跳过
我不喜欢我的思路被别人牵着走。
力扣的题目有时会有一些折叠起来的提示,我不喜欢去看这些提示。或者说在我觉得我的思路还没有真正走到死路之前,我是不会去看提示的。
要是看了提示,我的思路常常会被固化,会被那些提示牵着走,虽然说最终解出题目的概率会增大,但是或许我自己一开始的思路比提示的思路是更优解呢?谁也说不准。
在解出这道题时,我还没有看过力扣给的题目提示,所以,我会完全按照我自己的思路来讲解这道题。
一开始看到这道题,我想到了什么?
① 木桶原理[短板理论]:一个木桶存水量的多少,不取决与最长的木片的长度,而是取决于最短的木片的长度(以前的木桶是用多个木片围起来而形成的)。这道题也是存水问题,仔细看一下上面的直方图,就更能理解 [取决于最短的木片的长度] 的含义了(图中红色部分为可存水的部分)
② 高度大于 0 的柱子才能做为“木桶的木片”:高度为 0 的柱子是兜不住水的,例如 height[0]、height[2]
③ 存水量的多少取决于高度差
OK,开始解题
- 按照木桶原理,我可以先选中一个柱子 a,然后再找出另外一根柱子 b,要求 b 的高度大于等于 a,即
height[b] >= height[a]
。这样的话,a 和 b 之前就能形成一个凹的区域来存水 - a 和 b 之前存水量的多少取决于 a 的高度.假设 a 和 b 之前还存在 c、d 柱子(当然,
height[c] < height[a]
,height[d] < height[a]
),则 a 和 b 之前的存水量 =(height[a] - height[c]) + (height[a] - height[d])
,即 a 与它们的高度差之和 - “桶”的寻找要有方向性以保证不会计算到重复的水量。例如,我从左到右计算水量,那么我先选中一个柱子 a,向右找到了另外一个符合条件的柱子 b,计算出它们之前的存水量;则下次以 b 为“柱子 a”,向右继续寻找符合条件(
height[x] >= height[b]
)的柱子
下面我给出以这种思路而写出的代码:
public int trap(int[] height) {
if (height == null) {
return 0;
}
int result = 0;
// 从左往右求水量
for (int i = 0; i < height.length; ) {
if (height[i] != 0) {
int j = i + 1;
int waterVolume = 0;
// 往右寻找高度大于等于height[i]的柱子,并记录下沿途柱子与height[i]的高度差之和
while (j < height.length && height[j] < height[i]) {
waterVolume += (height[i] - height[j]);
j++;
}
if (j == height.length) {
// 说明从i开始往右的区域不能形成height[i]为最短木板的桶,兜不住水
i++;
} else {
result += waterVolume;
// 寻找下一个桶
i = j;
}
} else {
// 跳过左边界高度为0的柱子
i++;
}
}
return result;
}
okk,直到现在我发现我的思路还是没啥问题的,我在力扣上也写了两个测试用例来测试上面的代码,发现没问题。
但是,但是,这并不是十分正确的答案! 上面的代码失败在了下面的测试用例中:
很明显,这个直方图的存水量是 1,但是上面的代码的输出结果是 0
真正正确的答案
因为考虑不全,导致提交了一次失败的提交[又拉低了提交通过率,心痛]
失败的原因在于,没有考虑到最高柱子的存在,老是把重点放在了“短木片”上面了。因为你如果选中最高的柱子做为起始柱子,那么你向右寻找,不管怎么找都不会找到符合条件的另一个柱子(除非有多个最高柱子)。
所以,我们不如转变一下寻找的方向,不再是从左到右寻找或者从右到左寻找,而是从两端向最高的柱子聚拢寻找。更改后的代码如下所示:
class Solution {
public int trap(int[] height) {
if (height == null) {
return 0;
}
int result = 0;
int highest = findHighest(height);
// 从左往右求水量
for (int i = 0; i < highest; ) {
if (height[i] != 0) {
int j = i + 1;
int waterVolume = 0;
// 往右寻找高度大于等于height[i]的柱子,并记录下沿途柱子与height[i]的高度差之和
while (j < highest && height[j] < height[i]) {
waterVolume += (height[i] - height[j]);
j++;
}
result += waterVolume;
i = j;
} else {
// 跳过左边界的左边高度为0的柱子
i++;
}
}
// 从右往左求水量
for (int i = height.length - 1; i > highest; ) {
if (height[i] != 0) {
int j = i - 1;
int waterVolume = 0;
// 往右寻找高度大于等于height[i]的柱子,并记录下沿途柱子与height[i]的高度差之和
while (j > highest && height[j] < height[i]) {
waterVolume += (height[i] - height[j]);
j--;
}
result += waterVolume;
i = j;
} else {
// 跳过右边界的右边高度为0的柱子
i--;
}
}
return result;
}
// 找出最高的柱子的下标
private int findHighest(int[] height) {
int index = 0;
for (int i = 1; i < height.length; i++) {
if (height[i] > height[index]) {
index = i;
}
}
return index;
}
}
这是正确的答案,各位可看一看
最后
解完题之后,我看了一下力扣给出的提示,其中第 1 个提示就说到:直方图中最高的长方形起什么作用?如果我要是一开始就看到了提示,或许就能避免那次失败提交了。但是,凭自己的思路找到的正确答案,不更香吗?
这道题虽说是困难题,但是因为力扣给出了直观的图形给我们观察,难度降低了不少。
最后还是希望大家多多 AC 🤗