典中典之接雨水问题 「青训营 X 码上掘金」

44 阅读3分钟

当青训营遇上码上掘金
当我看到主题四我乐了,这不就是LeetCode经典的接雨水问题么hh。本文介绍三种常用的解法,抛砖引玉,更多解法可以去看这篇文章:详细通俗的思路分析,多解法,接雨水问题还是值得好好推敲的。

题干

主题 4:攒青豆

现有 n 个宽度为 1 的柱子,给出 n 个非负整数依次表示柱子的高度,排列后如下图所示,此时均匀从上空向下撒青豆,计算按此排列的柱子能接住多少青豆。(不考虑边角堆积)

image.png

以下为上图例子的解析:

输入:height = [5,0,2,1,4,0,1,0,3]  
输出:17  
解析:上面是由数组 [5,0,2,1,4,0,1,0,3] 表示的柱子高度,在这种情况下,可以接 17 个单位的青豆。

解法一:暴力

遇到一道题我们首先可以尝试暴力解,这通常是思考的起点。

对于本题来说,要求出所有青豆的数量,可以先计算每行青豆的数量,然后进行累加。参考代码如下:

复杂度分析

时间复杂度:O(n*m),取决于行数及最大高度

空间复杂度:O(1),不需要额外的存储空间

同样,我们可以按列来求,先计算出每列能接多少青豆,再进行累加。这样的时间复杂度为:O(n*2)

解法二:动态规划

对于解法一,数据量小时可以轻松通过,但当数据量过大将会不可避免地超时。因此必须考虑其他解法。

在这里,我们可以考虑空间换时间的方式,先从左到右遍历数组,记录左边最高的墙;再从右到左遍历数组,记录右边最高的墙。之后再从左到右遍历一次数组,根据当前位置左边最高的墙与右边最高的墙,按照短板理论,计算出当前列能接到的最大数量青豆。

这种解法其实就是按列求时的优化,参考代码如下:

复杂度分析

时间复杂度:O(n)

空间复杂度:O(n),需要记录每列左边和右边最高的墙

解法三:动态规划优化

对于解法二,可以发现其实只要记录右边最高的墙,之后在计算阶段记录左边最高的墙就可以了,这样可以少一次循环。参考代码如下:(这次用js写)

复杂度分析

时间复杂度:O(n)

空间复杂度:O(n),需要记录每列左边和右边最高的墙

总结

接雨水是一道非常好的题目,有多种解法,也有很多优化空间,本文所列举的解法并不是最优,空间复杂度可优化。借助本题可以收获一系列算法思路,感受到算法的乐趣。