LeetCode 407题"接雨水II"是经典"接雨水"问题的三维扩展版本。与一维版本不同,这里我们需要处理的是一个二维高度图,计算其中能够接住的雨水总量。这个问题在实际中有许多应用场景,比如地形蓄水计算、建筑排水设计等。
给定一个m x n的二维矩阵heightMap,其中每个元素表示该位置的高度。计算这个地形能够接住多少雨水。例如:
输入:
[
[1,4,3,1,3,2],
[3,2,1,3,2,4],
[2,3,3,2,3,1]
]
输出:4
这个问题可以使用"最小堆+广度优先搜索(BFS)"的方法解决。核心思想是:
这种方法确保了总是从最低的边界点开始处理,保证水能够从最低处开始蓄积。
首先定义一个内部类Cell,用于存储位置和高度信息:
java复制class Cell {
int row;
int col;
int height;
public Cell(int row, int col, int height) {
this.row = row;
this.col = col;
this.height = height;
}
}
这个类封装了矩阵中的每个单元格,包含行索引、列索引和高度值。
java复制if (heightMap == null || heightMap.length < 3 || heightMap[0].length < 3) {
return 0;
}
int m = heightMap.length;
int n = heightMap[0].length;
boolean[][] visited = new boolean[m][n];
PriorityQueue<Cell> pq = new PriorityQueue<>((a, b) -> a.height - b.height);
这里进行了必要的边界检查,矩阵至少需要3x3大小才能形成蓄水空间。然后初始化了访问标记数组和最小堆。
java复制// 将边界上的点加入优先队列
for (int i = 0; i < m; i++) {
pq.offer(new Cell(i, 0, heightMap[i][0]));
pq.offer(new Cell(i, n - 1, heightMap[i][n - 1]));
visited[i][0] = true;
visited[i][n - 1] = true;
}
for (int j = 1; j < n - 1; j++) {
pq.offer(new Cell(0, j, heightMap[0][j]));
pq.offer(new Cell(m - 1, j, heightMap[m - 1][j]));
visited[0][j] = true;
visited[m - 1][j] = true;
}
这段代码将矩阵的四条边上的所有点加入最小堆,并标记为已访问。这是算法的起点,从边界开始向内处理。
java复制int res = 0;
int[][] dirs = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};
while (!pq.isEmpty()) {
Cell curr = pq.poll();
for (int[] dir : dirs) {
int x = curr.row + dir[0];
int y = curr.col + dir[1];
if (x >= 0 && x < m && y >= 0 && y < n && !visited[x][y]) {
visited[x][y] = true;
// 如果邻居的高度小于当前高度,则可以蓄水
res += Math.max(0, curr.height - heightMap[x][y]);
// 将邻居加入优先队列,高度取最大值(因为水会填平)
pq.offer(new Cell(x, y, Math.max(curr.height, heightMap[x][y])));
}
}
}
return res;
这是算法的核心部分,不断从堆中取出最低点,检查其四个方向的相邻点。如果可以蓄水(相邻点高度低于当前点),则计算蓄水量,并将相邻点加入堆中。
该算法的时间复杂度主要由优先队列的操作决定:
因此总时间复杂度为O(mn log(mn)),因为堆的大小最多为O(mn)。
空间复杂度主要来自:
因此总空间复杂度为O(mn)。
这个算法可以应用于:
虽然这个解法已经相当高效,但仍有优化空间:
提示:在实现这类算法时,建议先处理简单案例,再逐步增加复杂度。例如先处理3x3矩阵,确认基本逻辑正确后再测试更大规模数据。
这个问题可以进一步扩展到三维空间,计算三维网格中的"接雨水"量。解法思路类似,但需要考虑更多相邻方向(从4个增加到6个)。
如果高度图会随时间变化,可以扩展算法支持动态更新。每次高度变化后,只需要重新计算受影响区域的蓄水量。
可以修改问题,考虑有多个水源点的情况。这需要调整初始边界点的选择策略。
暴力法需要为每个点找到其周围的最低边界,时间复杂度为O(m^2n^2)。相比之下,最小堆方法效率更高。
类似一维接雨水问题,可以尝试用动态规划解决。但二维情况下,动态规划难以找到合适的状态转移方程。
有研究提出使用并查集数据结构解决此问题,但实现复杂度较高,实际性能提升有限。
设计测试用例时应考虑:
该算法可以集成到GIS系统中,用于地形分析和水文建模。实际应用中需要考虑:
在游戏引擎中,可以用于:
建筑师可以使用类似算法:
理解这个算法的一个好方法是可视化其执行过程:
可视化可以帮助直观理解为什么总是从最低的边界点开始处理,以及水是如何"填平"低洼区域的。