42、接雨水

题目描述:

给定 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

 

解题思路:

  1. 一开始的想法就是寻找极大值点,两个距离大于一的极大值点之间的凹陷就是可以盛接雨水的地方;
  2. 考虑到202341这样的情况,4是极大值点,但盛接雨水的是202,和4这个极大值点没关系,所以调整思路;
  3. 还是使用双指针去做,盛接雨水的左边界高度 left 和右边界高度 right,当 left 确定时,找一个合适的 right 去计算面积;
  4. 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值遍历完整个数组
  5. 对第4步做一个归纳,即把整个数组分成了类似于两类
    第一类 203形式,右边界值大于左边界值;
    第二类 302形式,左边界值大于右边界值;
    整个function就是对这两种类型进行了处理

 

需要改进点:

  1. 对于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;
}

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值