解决二维接雨水问题的Java代码示例,并在每行添加注释以帮助你理解。
public class Solution {
public int trapRainWater(int[][] heightMap) {
// 如果高度图为空,或者高度图的行数或列数小于等于2,则无法接住雨水
if (heightMap == null || heightMap.length <= 2 || heightMap[0].length <= 2) {
return 0;
}
// 获取行数和列数
int m = heightMap.length;
int n = heightMap[0].length;
// 使用优先队列来存储边界的单元格
PriorityQueue<Cell> pq = new PriorityQueue<>((a, b) -> a.height - b.height);
boolean[][] visited = new boolean[m][n];
// 将所有边界单元格放入优先队列
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;
}
// 定义四个方向的数组
int[][] directions = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}};
int waterTrapped = 0;
// 处理优先队列中的每个单元格
while (!pq.isEmpty()) {
Cell cell = pq.poll(); // 取出高度最低的单元格
for (int[] dir : directions) {
int row = cell.row + dir[0];
int col = cell.col + dir[1];
// 检查相邻单元格是否在边界内并且未被访问
if (row >= 0 && row < m && col >= 0 && col < n && !visited[row][col]) {
// 计算当前单元格可以存储的水量
waterTrapped += Math.max(0, cell.height - heightMap[row][col]);
// 更新相邻单元格的高度为当前单元格和自身高度的最大值
pq.offer(new Cell(row, col, Math.max(heightMap[row][col], cell.height)));
visited[row][col] = true; // 标记为已访问
}
}
}
return waterTrapped; // 返回总的储水量
}
// 定义一个内部类来存储单元格的信息
class Cell {
int row, col, height;
Cell(int row, int col, int height) {
this.row = row;
this.col = col;
this.height = height;
}
}
}
这个代码利用优先队列(最小堆)来动态追踪当前边界的最低高度,逐步“填平”低洼区域,计算总的储水量。
解题思路
-
边界处理:由于水无法从边界流出,首先需要处理边界上的单元格。将所有边界单元格放入一个优先队列中(最小堆),用于动态追踪当前边界的最低高度。
-
优先队列(最小堆):利用优先队列从最低的高度开始处理,以确保每次处理的都是当前最低的边界。这样可以模拟水从低洼区域向高处流动的过程。
-
遍历和更新:
- 从优先队列中取出高度最低的单元格。
- 检查其四个方向的相邻单元格:
- 如果相邻单元格未访问过,计算可以存储的水量:
max(0, 当前高度 - 相邻单元格高度)
。 - 更新相邻单元格的高度为当前高度和自身高度的最大值(模拟水的填充)。
- 将更新后的相邻单元格加入优先队列。
- 如果相邻单元格未访问过,计算可以存储的水量:
- 标记相邻单元格为已访问。
-
累加水量:在遍历过程中累加可以存储的水量,最终得到总的储水量。
算法复杂度
- 时间复杂度:O(m * n * log(m * n)),其中
m
和n
分别是高度图的行数和列数。每个单元格最多被处理一次,且每次操作都涉及优先队列的插入和删除。 - 空间复杂度:O(m * n),用于存储访问状态和优先队列。
总结
这个问题是对一维接雨水问题的拓展,引入了二维的高度图。通过利用优先队列,我们能够有效地处理地形的边界和低洼区域,实现动态填充水的过程。这个方法确保了每个位置的水高度是由其最低的边界决定的。