题目描述:
给定 n 个非负整数表示每个宽度为 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
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/trapping-rain-water
解题思路:
- 一开始的想法就是寻找极大值点,两个距离大于一的极大值点之间的凹陷就是可以盛接雨水的地方;
- 考虑到202341这样的情况,4是极大值点,但盛接雨水的是202,和4这个极大值点没关系,所以调整思路;
- 还是使用双指针去做,盛接雨水的左边界高度 left 和右边界高度 right,当 left 确定时,找一个合适的 right 去计算面积;
- left 确定,在右侧找一个合适的 right 值
(1) 能够找到一个 right > left 的情况,则可以直接计算这一部分的雨水面积,例如 203,类似20203也可以直接归纳成同 一类。计算完这一部分之后,将 right 值赋给 left,继续寻找合适的新 right 值;
(2)找不到 right > left 的情况,为了应付这种情况,新引入一个值 middle 。因为在遍历完整个数组之前,是不确定会不 会有一个值大于 left 的,所以在遍历的过程中,先将能够找到的最大值赋给 middle,同时继续遍历数组去寻找那个 可能存在的大于 left 的 right 值。遍历完整个数组之后,如果的确找不到一个比 left 大的 right 值,则使用 [left,middle] 这个区间进行计算。随后将 middle 值赋给 left,继续进行下一次遍历,寻找新的右边界;
(3)重复步骤(1)、(2),直至left值遍历完整个数组 - 对第4步做一个归纳,即把整个数组分成了类似于两类
第一类 203形式,右边界值大于左边界值;
第二类 302形式,左边界值大于右边界值;
整个function就是对这两种类型进行了处理
需要改进点:
- 对于543202类型的数据,没有做到最佳处理,5作为left值遍历完一遍之后,最佳策略是直接把第一个 2 作为 left 值,但是在代码中,是5、4、3、2依次作为 left 值,每次都要进行依次遍历,直到做完四次遍历,才找到 202 这个区间。
int trap(int* height, int heightSize)
{
int sum_all = 0; //雨水体积和
int max_old[2] = { 0,0 }, max_now[2] = { 0,0 }; // [0] 数值 [1]下标
int max_temp[2] = { 0 };
int k_s = 1;
char left_right_flag = 0;
if (heightSize >= 2)
max_old[0] = height[0];
else
return 0;
while (k_s < heightSize)
{
if (height[k_s] >= max_now[0]) //把等于合并进来,能把类似 20202之类的合并成一个区间 {
{
max_now[0] = height[k_s]; //now值决定右边界
max_now[1] = k_s;
if (max_now[0] >= max_temp[0]) //左边界值比较大,在寻找右侧比该值大的值的过程中
{ //记录下比较大的值
max_temp[0] = max_now[0];
max_temp[1] = max_now[1];
}
//第一种情况:左边界值小于等于右边界值,此时可以直接计算
if (max_now[0] >= max_old[0] )
{
if (max_now[1] - max_old[1] > 1 //102这样的形式,则计算雨水体积
&& max_old[0] > 0) //避免002这样的形式
{
for (int i = max_old[1] + 1; i < max_now[1]; i++)
{
sum_all += max_old[0] - height[i];
}
}
//12这样的形式,则不做责任操作
max_old[0] = max_now[0];
max_old[1] = max_now[1];
max_now[0] = 0;
max_now[1] = 0;
}
//第二种情况:左边界值大于当前右边界值
if (left_right_flag == 2) //已遍历完成,的确左边界值大于右边界值
{
if (max_temp[1] - max_old[1] > 1)
{
for (int i = max_old[1] + 1; i < max_temp[1]; i++)
{
sum_all += max_temp[0] - height[i];
}
}
max_old[0] = max_temp[0];
max_old[1] = max_temp[1];
max_now[0] = 0;
max_now[1] = 0;
max_temp[0] = 0;
max_temp[1] = 0;
left_right_flag = 0;
}
else if (max_old[0] > max_now[0] //左边界值大于右边界值,且未遍历完成
&& max_now[1] > 0) //排除右边界值数组刚清零的情况
left_right_flag = 1;
else
left_right_flag = 0;
}
k_s++;
if (k_s == heightSize && left_right_flag == 1)
{
k_s = max_temp[1];
left_right_flag = 2;
}
}
return sum_all;
}