首页

▼ 算法合集

▼ 加密算法

▼ 凯撒密码

凯撒密码

▼ Hill密码算法

Hill密码算法

▼ 多项式哈希算法

多项式哈希算法

▼ Rail Fence Cipher

Rail Fence Cipher

▼ Graph算法

▼ 关节点

关节点

▼ Bellman-Ford算法

Bellman-Ford算法

▼ 广度优先搜索

广度优先搜索

▼ GraphBridges

桥接模式

▼ 深度优先搜索

深度优先搜索

▼ 检测循环

检测循环

▼ Dijkstra算法

Dijkstra算法

▼ Eulerian Path

欧拉路径

▼ Floyd-Warshall算法

Floyd-Warshall算法

▼ HamiltonianCycle

Hamiltonian Cycle

▼ Kruskal算法

Kruskal算法

▼ Prim算法

Prim算法

▼ 强连通分量

强连通分量

▼ 拓扑排序

拓扑排序

▼ Travelling Salesman Problem

Travelling Salesman

▼ 图像处理算法

▼ Seam Carving算法

内容感知缩放算法

▼ 链表

▼ 反向遍历

反向遍历

▼ GraphTraversal

GraphTraversal

▼ 数学算法

▼ 二进制浮点数

BinaryFloatingPoint

▼ 位操作

位操作算法

▼ 复数

复数

▼ 欧几里得算法

Euclidean Algorithm

▼ Euclidean Distance

欧几里得距离

▼ 阶乘算法

阶乘算法

▼ 快速幂算法

快速幂算法

▼ Fibonacci数列

斐波那契数列

▼ 傅里叶变换

Fourier变换

▼ Horner法

霍纳法则

▼ 整数划分

整数划分

▼ 判断是否为2的幂

判断是否为2的幂

▼ 最小公倍数

最小公倍数

▼ Liu Hui

Liu Hui

▼ 矩阵

Matrix

▼ Pascal三角形

Pascal三角形

▼ Primality Test

素数测试

▼ 质因数

质因数

▼ 弧度计算

弧度计算

▼ 埃拉托色尼筛法

埃拉托色尼筛法

▼ SquareRoot

SquareRoot

▼ MachineLearning

▼ K均值算法

K均值算法

▼ K近邻算法

K近邻算法

▼ 搜索算法

▼ 二分查找算法

二分查找

▼ 插值搜索算法

插值搜索算法

▼ 跳跃搜索算法

跳跃搜索算法

▼ 线性搜索

线性搜索算法

▼ 集合

▼ 笛卡尔积

笛卡尔积

▼ 组合总和

组合总和

▼ 组合算法

组合算法

▼ Fisher-Yates洗牌算法

Fisher-Yates洗牌算法

▼ 背包问题

背包问题

▼ 最长公共子序列

最长公共子序列

▼ 最长递增子序列

最长递增子序列

▼ 最大子数组

最大子数组

▼ 排列组合

排列组合

▼ 幂集

幂集算法

▼ 最短公共超序列

最短公共超序列

▼ Sorting Algorithms

▼ 冒泡排序

冒泡排序

▼ 桶排序算法

桶排序算法

▼ 计数排序算法

计数排序

▼ 堆排序算法

堆排序

▼ 插入排序

插入排序

▼ 归并排序

归并排序

▼ 快速排序算法

快速排序算法

▼ 基数排序

基数排序

▼ 选择排序算法

选择排序算法

▼ 希尔排序

希尔排序

▼ 统计学

▼ 加权随机

加权随机算法

▼ 字符串算法

▼ Hamming距离

Hamming距离

▼ KnuthMorrisPratt算法

Knuth-Morris-Pratt算法

▼ LevenshteinDistance

Levenshtein距离

▼ 最长公共子串

最长公共子串

▼ 回文检测算法

回文检测算法

▼ Rabin-Karp算法

Rabin-Karp算法

▼ 正则表达式匹配

正则表达式匹配

▼ Z算法

Z算法

▼ Tree Data Structure

▼ 广度优先搜索

广度优先搜索

▼ 深度优先搜索

深度优先搜索

▼ 未分类

▼ 最佳买卖股票时机

最佳买卖股票时机

▼ 汉诺塔算法

HanoiTower

▼ 跳跃游戏算法

跳跃游戏

▼ KnightTour

骑士巡逻

▼ N皇后问题

N皇后问题

▼ 雨水收集

雨水收集

▼ 递归楼梯问题

递归楼梯问题

▼ 方阵旋转

方阵旋转

▼ 独特路径

UniquePaths

▼ 数据结构

▼ BloomFilter算法

布隆过滤器

▼ 不相交集数据结构

Disjoint Set

▼ 双向链表

双向链表

▼ Graph

Graph算法

▼ 哈希表

哈希表

▼ Heap数据结构

Heap数据结构

▼ 链表

链表

▼ LRU缓存

LRU缓存

▼ 优先队列

优先队列

▼ 队列

队列

▼ 栈结构

栈结构

▼ Tree Data Structure

树结构

▼ AVL树

AVL树

▼ 二叉搜索树

二叉搜索树

▼ Fenwick树

Fenwick树

▼ 红黑树

红黑树

▼ 线段树

SegmentTree

▼ Trie数据结构

Trie数据结构

雨水滞留问题(捕获雨水)

给定一个表示地形图中台阶高度的非负整数数组,其中每个台阶的宽度为1,计算雨后它能捕获多少雨水。

示例

示例 #1

输入:arr[] = [2, 0, 2]
输出:2
结构如下所示:

| |
|_|

我们可以在中间间隙捕获2个单位的雨水。

示例 #2

输入:arr[] = [3, 0, 0, 2, 0, 4]
输出:10
结构如下所示:

     |
|    |
|  | |
|__|_| 

我们可以在3和2之间捕获“3*2个单位”的雨水,在2上面捕获“1个单位”,在2和4之间捕获“3个单位”。也可以参见下面的图表。

示例 #3

输入:arr[] = [0, 1, 0, 2, 1, 0, 1, 3, 2, 1, 2, 1]
输出:6
结构如下所示:

       | 
   |   || |
_|_||_||||||

在第一个1和第二个1之间捕获“1个单位”的雨水,在最后一个1和倒数第二个2之间捕获“4个单位”的雨水。

算法

如果一个元素左边和右边都有更高的台阶,则该元素可以存储水。我们可以通过找到左边和右边台阶的最大高度来计算每个元素可以存储的水量。例如,考虑数组[3, 0, 0, 2, 0, 4],我们可以捕获“3*2个单位”的雨水在3和2之间,“1个单位”在2上面和“3个单位”在2和4之间。也可以参见下面的图表。

方法1:暴力破解

直觉

对于数组中的每个元素,我们找到它雨后能捕获的最大水量,这等于两边最大高度的最小值减去它自己的高度。

步骤

  • 初始化answer = 0
  • 从左到右遍历数组:
  • 初始化max_left = 0max_right = 0
  • 从当前元素开始到数组开头更新:max_left = max(max_left, height[j])
  • 从当前元素开始到数组结尾更新:max_right = max(max_right, height[j])
  • min(max_left, max_right) − height[i]添加到answer

复杂度分析

时间复杂度:O(n^2)。对于数组中的每个元素,我们遍历左右两侧各一次。

辅助空间复杂度:O(1)的额外空间。

方法2:动态规划

直觉

在暴力破解中,我们再次反复遍历左右两侧,只是为了找到该索引之前的最高台阶大小。但是,这个值可以被存储起来。看吧,这就是动态规划。

因此,我们可以在O(n)时间内预计算出每个台阶左右两侧的最高台阶。然后使用这些预计算出的值来找到数组中每个元素的水量。

该概念如图所示:

DP 捕获雨水

步骤

  • 在数组的第i个位置之前,找到从左边开始的最高台阶left_max[i]
  • 在数组的第i个位置之前,找到从右边开始的最高台阶right_max[i]
  • 遍历height数组并更新answer
  • min(max_left[i], max_right[i]) − height[i]添加到answer

复杂度分析

时间复杂度:O(n)。我们使用两次O(n)的时间迭代来存储最多高度,最后使用存储的值更新answer

辅助空间复杂度:O(n)的额外空间。比方法1多了left_maxright_max数组的额外空间。

参考资料