Trapping rain water

来源:互联网 发布:linux查cpu核数 编辑:程序博客网 时间:2024/05/01 08:27

题目描述

这道题可以借鉴直方图中求最大矩形面积的思想,是一道非常经典的题目,难度非常大,但是又经常在面试中出现。

 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.For example,Given [0,1,0,2,1,0,1,3,2,1,2,1], return 6. 

这里写图片描述

分析

要理解本文的方法,可以先去看看陈利人老师提出的O(n)时间内解决直方图中最大矩形面积的问题很神奇的解法!怎么求柱状图中的最大矩形? ,如果网页上面无法查看图片,可以关注陈利人老师的微信公众号,上面还有一些其他的经典面试题。

算法的核心思想在于维护了一个非递减的栈,并考虑了相邻矩形之间的高度关系,具体请查看陈利人老师原文,这里不再详细讲解,可以理解了之后再继续阅读本文,也可以直接阅读本文。我将尝试着跟大家讲清楚!这里列出陈老师文章里的一张经的图片这里写图片描述

回到本题:
什么样的情况才可能存水呢?只有两边的都比自己高,才有可能存水!如下图所示。
这里写图片描述
但是图形肯定不是如此简单的,如下图所示
这里写图片描述

使用一个非递增的栈stack, 也就是说(遍历数组遇到height[i]比当前栈顶元素更大的元素时,需要将栈中所有比height[i]小的元素出栈,只有这样才能维持栈的非递增顺序),那么在这些元素出栈时如何高效的计算和存储water量呢?
逐步填平凹陷的地方,如下图所示:
这里写图片描述

上面给出了计算的顺序,下面我们来逐步解释!
给定三个重要的量:

  • 当前编号i
  • 当前栈顶元素bot=stack[-1]
  • 栈顶元素出栈之后新的栈顶元素stack[-1]

根据这三个量的变化情况演示上面的图:

  1. stack是非递增的,可以知道当i=7时,stack=[2,3,4,5,6].此时有stack[-1]=5,bot=6,i=7,先计算途中紫色部分;此时栈顶元素5对应的高度还是小于i=7的,继续出栈,stack[-1]=4,bot=5,i=7,计算绿色部分。然后7本身是最小的了,入栈
  2. i=8入栈
  3. i=9,stack=[2,3,4,7,8],8出栈,计算淡紫色部分;接着7出栈,计算蛋黄色部分;然后4出栈,计算红色部分;最后3出栈,计算桃红色部分;然后2出栈,此时栈已经为空,无需再计算。即得到了总体积水量。

注意:bot始终代表stack[-1]i之间已经填平后的高度(注意分析这句话),新的积水量:

(min(height[i],height[stack[1]])height[bot])(istack[1]1)

将所有的积水量相加就是最后的结果。

编码

stack = []        i = 0        maxwater = 0        while (i < len(height)):            if len(stack) == 0 or (height[i] <= height[stack[-1]]):                stack.append(i)                i += 1            else:                bot = stack[-1]                stack.pop()                if len(stack) == 0:                    water = 0                else:                    water = (min(height[i], height[stack[-1]]) - height[bot]) * (i - stack[-1] - 1)                maxwater += water            return maxwater     

上述代码在leetcode上面并不是最优的,有兴趣的童鞋可以看看大神们的解法!下面我们跟踪一下代码,帮助理解算法,已经读懂的同学不用再看下面的!

示例

  1. 开始时 i=0,stack=[ ], 根据算法stack.append(i) i++
  2. i=1,stack=[0],而栈顶元素高度小于当前i的高度,出栈操作并且计算water=0: 这里写图片描述
    当再次进入while时,由于栈为空,直接将1进栈,且i++
  3. i=2,stack=[1],i=2,当前元素高度小于栈顶元素高度,i入栈
  4. i=3,stack=[1,2], 栈顶元素高度小于i的高度(height[stack[-1]<height[i]]),计算存水量: bot=stack.pop()bot=1,栈非空:[0](min(height[stack[-1]],height[i])-height[bot])*(i-stack[-1]-1)=(min(height[1],height[2])-height[2])*(2-0-1)=1这里写图片描述
  5. i=3,stack=[1],当前元素i的高度大于栈顶,则出栈,此时栈为空,水量为0
  6. i=3,stack=[],直接将i入栈,且i++
  7. i=4,stack=[3],入栈且i++
  8. i=5,stack=[3,4],还是入栈操作
  9. i=6,stack=[3,4,5],此时需要计算water了。先将5出栈计算的雨量1.然后栈顶元素4, 该入栈了。这里写图片描述
  10. i=7,stack=[3,4],大于栈顶元素,应该计算water,此时计算的是浅黄色的部分(原理我们稍后解释)这里写图片描述
  11. i=7,stack=[3],出栈后为空,water=0,在下一步的while中重新将i=7入栈,一直继续下去…
原创粉丝点击