求可以存储的水量,那么什么情况才能存到水呢?只有两头高,中间小的才能够存储到水,所以求能存储的水量的大
小,得先求得一共有多少的峰顶,然后根据峰顶的值来讨论,可能的情况如图所示
可以发现,对于前两种情况,能存多少水量由第一个峰值决定,而对于第三种情况,又会分为这么几种情况
对于第一行的情况来说,能存水量决定于第二块和第三块,分别是两个区间,对于第二行来说,存水量就决定于第三块
了,即大于第二块,但是小于第一块的情况,是一个区间,所以综合上面所有的情况就会得到:
对于某一个峰值:
当他后面存在大于他的峰值的时候,就将中间的储水量加起来
当他后面的柱子都不大于他时,找到他后面的小于他的最大的那根柱子,求得中间水量
之后从后面那个柱子开始,继续上述操作
这样就可以得到最终的储水量了
class Solution {
public:
int trap(vector<int>& height) {
int sum = 0, i = 0, n = height.size() - 1;
/*
如果前面都是上升的,或者是平的地形,那么他是不可能储水的,去掉
*/
while(i + 1 < n && height[i] <= height[i + 1])
{
++i;
}
/*
对于后面的下降的或者是平的地形,也不能储水,也去掉
*/
while(n > 0 && height[n] <= height[n - 1])
{
--n;
}
/*
当去掉前后之后,如果中间没有空位了,那就是两个峰值重合了,对应的就是一个尖尖的山,这种
情况是不会有储水的,直接返回即可
*/
if(n - i <= 1)
{
return sum;
}
vector<int> temp; // 用来存放峰值在原来地方的下标
temp.push_back(i); // 由于一开始先上了一次山,所以 i 处必然是一个峰值
/*
通过一次下山和一次上山得到下一个峰值点,并且存到temp中
*/
do
{
while(i + 1 <= n && height[i] >= height[i + 1])
{
++i;
}
while(i + 1 <= n && height[i] <= height[i + 1])
{
++i;
}
temp.push_back(i);
}while(i + 1 <= n);
/*
l 用来保存还没有使用的左边的峰值,max 用来保存 他 右侧不大于自己的相对来说最大的那个峰值
r 用来保存 max 这个峰值在 temp 中保存的位置,为了控制后面的下标 i 的循环跳转
*/
int l = temp[0], max = temp[1], r = 1;
for(i = 1; i < temp.size(); ++i)
{
/*
用于处理前面图示中的后面有大于当前峰值的柱子
*/
if(height[temp[i]] >= height[l])
{
for(int r = l + 1; r < temp[i]; ++r)
{
if(height[l] - height[r] > 0)
{
sum += height[l] - height[r];
}
}
l = temp[i];
if(i + 1 < temp.size())
{
max = temp[i + 1];
r = i + 1;
}
continue;
}
/*
用于处理前面的后面没有比他大的柱子的情况
*/
if(height[max] <= height[temp[i]]) // 不断的动态更新 max 的值
{
max = temp[i];
r = i;
}
/*
当 i 走到 temp 的最后一位还没有找到比当前大的柱子,那么就开始从当前 max 遍历,符合前面的
图示的第三种情况的第二种情况。
*/
if(i == temp.size() - 1)
{
for(int r = l + 1; r < max; ++r)
{
if(height[max] - height[r] > 0)
{
sum += height[max] - height[r];
}
}
i = r;
l = max;
if(i + 1 < temp.size())
{
max = temp[i + 1];
r = i + 1;
}
else
{
return sum;
}
}
}
return sum;
}
};