LeetCode 42 接雨水 Trapping Rain Water

LeetCode 42 接雨水

Given n non-negative integers representing an elevation map where the width of each bar is 1, compute how much water it is able to trap after raining.

题目:本题输入一个由非负整数组成的数组,表示地面上若干柱子的高度,宽度均为1,下雨时,比两侧低的凹陷部分就会积水,求积水量。

分析:

要求某一个柱子的位置的积水量,等价于求这个柱子能够积水的最高水位(不漏出去)减去柱子本身的高度。例如 3 1 3 这样的高度的三个柱子,中间的1能积的最高水位就是3,否则水就从两侧流走,那么他的积水量就是3-1=2.本题要求的结果就是所有柱子的积水量之和。
对于每一个柱子,可以以他为中心,分别向左右去寻找一个边界,从待求点开始向左能找到的最高位置就是左侧的边界,右侧也是同理,这两个边界的较低值,就是待求点蓄水的上限。
例如,高度分别是 2 5 3 1 3 6 4
我们想知道中间1的位置的积水量,可以分别向左右找最高的位置,得到左边界是5.同理右边界找到是6,那么取二者较小值,也就是5。则1这个位置积水量是5-1=4
但是对于每个点都这样左右遍历寻找边界点,时间复杂度是O(n^2),很慢。

换一个思路,我们实际上可以通过从左向右和从右向左各自遍历一遍,得到两个数组,记录每个点左边和右边比自身大的最大高度,再从中选取较小的计算积水量,这样遍历2次数组,并引入O(n)的额外空间可以完成。

那么有没有更优的解法呢?也是有的,我们考虑从两边向中间的方式进行优化。

首先取两个索引left和right分别指向左右侧最高的柱子,也就是边界,初始化为第一个和最后一个柱子,再取一个cur索引,记录当前柱子;
在left小于right时,先判断left和right处的柱子高度,哪一个更低。二者之间的柱子水位高度的上限显然不能超过left和right高度的任何一个,否则水将从这一侧漏下去。
如果left更低,就意味着二者之间的柱子如果低于left处的高度,那么将会积水,且积水量的上限是left的高度。那么将cur置于left处,当cur的高度低于left高度,就将二者差值作为cur处的积水量,加在总的积水量上;反之,若cur处高度大于left高度,此时不能积水,并且从cur向右的所有柱子,其左边界实际上都是这个更高的值,因此,将left移动到cur处,并重新判断这个新的左边界和右边界哪一个更高。
同理如果right更低,right将作为此时的水位上限,cur从right处向左移动。同样,若cur高度低于right,则将有积水,量为right高度减去cur高度;若cur更高,意味着由此向左的右边界出现了变化,将right移动到cur位置,重新判断left和right的高度。
只到left和right相遇,意味着所有柱子均计算完成,此时返回累加的总量即可。

仍然以上述的 2 5 3 1 3 6 4为例:首先left位于最左边高度为2,right高度为4,此时left更低,因此cur从0开始向右;遇到了坐标为1的柱子高度为5,大于原来的left高度2,也就是说从这里向右的点的左边界是5,将left移动到此处;再次比较左右边界高度,发现右侧4更小,将cur置于右端向左移动,遇到高度为6的柱子,更新右边界为6;5<6因此急需从左开始,cur分别经历3,1,3都小于5,于是将差值,也就是积水量累加起来,2+4+2=8,此时cur来到右边界6的位置,它高于左边界5,因此将left更新到此处,左右相遇,计算完成,返回结果8.

C++实现如下:

int trap(vector<int>& height) {
  int sumWater = 0, left = 0, right = height.size()-1,cur =0;
  while (left < right) {
    if (height[left] < height[right]) {
      cur = left;
      while (height[left] >= height[cur] && cur < right) {
        sumWater += height[left] - height[cur];
        ++cur;
      }
      left = cur;
    }
    else {
      cur = right;
      while (height[right] >= height[cur] && cur > left) {
        sumWater += height[right] - height[cur];
        --cur;
      }
      right = cur;
    }
  }
  return sumWater;
    }

本解法核心问题就是从两边向中间的移动,两个边界较低的那个就是当前的水位上限,由它开始向另一方向移动,若低于这个高度就计算积水量,高于这个高度,就意味着出现了更高的边界,取代原来的成为新边界,然后在比较两侧哪个更低,只到左右边界相遇,遍历完所有柱子。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值