题目&示例
给你一个 m x n 的矩阵,其中的值均为非负整数,代表二维高度图每个单元的高度,请计算图中形状最多能接多少体积的雨水。
提示:
- m == heightMap.length
- n == heightMap[i].length
- 1 <= m, n <= 200
- 0 <= heightMap[i][j] <= 2 * 104
一、思路
分析题目,可以知道heightMap是一个二维数组,我门把数组的第一个下标看成横坐标,把数组第二个坐标看成纵坐标,把数组中对应两个下标的值看做长方体的高度,这样我们就可以 描述每一个长方体的位置和高度。
接下来我们可以这样处理:
- 首先解决特殊情况,当二维数组(heightMap)的行或列小于3时,显然不能蓄水。
int row = heightMap.length;
int col = heightMap[0].length;
if(row < 3 || col < 3) return 0;
- 将最外围的一圈长方体的信息(即外部节点)放入队列中,并按高度从小到大排列。
// 构建pq, 将最外圈放入pq,同时标记visited
// 队列中元素从小到大排列
PriorityQueue<int[]> queue = new PriorityQueue<int[]>((o1, o2) -> o1[2] - o2[2]);
boolean[][] visited = new boolean[row][col];
for (int i = 0; i < row; i++) {
for (int j = 0; j < col; j++) {
if (i == 0 || i == row - 1 || j == 0 || j == col - 1) {
queue.offer(new int[]{i, j, heightMap[i][j]});
visited[i][j] = true;
}
}
}
- 循环从队列中取出元素,由队列的性质可知,每次取出的定为最小的。然后再遍历其上下左右节点,若找到比该节点小的节点,就就灌水到与该节点等高,并计算其可以灌多少水,此时可以把这个灌了水的节点看作外部节点。然后把该节点出队,把该节点周围没有访问过的内部节点入队,即把它们看作新的外部节点。
// 循环从pq取出元素,遍历其左上右下结点,如果找到比他小的内部节点,就灌水到等高
// 元素出队,入队内部节点,标记visited
int res = 0;
while (!queue.isEmpty()) {
int[] cur = queue.poll(); // 最小节点出队
int[] dir = {-1, 0, 1, 0, -1};
// 循环判断其上下左右四个相邻节点: (-1,0)左,(0,1)上,(1,0)右,(0,-1)下
for (int i = 0; i < 4; i++) {
int nx = cur[0] + dir[i];
int ny = cur[1] + dir[i + 1];
// 判断是否为内部部节点,且没被访问过
if (nx >= 0 && nx < row && ny >= 0 && ny < col && !visited[nx][ny]) {
int temp = cur[2] - heightMap[nx][ny];
// 为内部节点,且比当前节点(外部节点)低,则可以蓄水
if(temp > 0){
res += temp;
}
// 将可以蓄水的节点作为外部节点入队,并标记为已经访问过
queue.offer(new int[]{nx, ny, Math.max(heightMap[nx][ny], cur[2])});
visited[nx][ny] = true;
}
}
}
return res;
}
二、完整代码
class Solution {
public int trapRainWater(int[][] heightMap) {
// 解决特殊情况
int row = heightMap.length;
int col = heightMap[0].length;
if(row < 3 || col < 3) return 0;
// 构建pq, 将最外圈放入pq,同时标记visited
// 队列中元素从小到大排列
PriorityQueue<int[]> queue = new PriorityQueue<int[]>((o1, o2) -> o1[2] - o2[2]);
boolean[][] visited = new boolean[row][col];
for (int i = 0; i < row; i++) {
for (int j = 0; j < col; j++) {
if (i == 0 || i == row - 1 || j == 0 || j == col - 1) {
queue.offer(new int[]{i, j, heightMap[i][j]});
visited[i][j] = true;
}
}
}
// 循环从pq取出元素,遍历其左上右下结点,如果找到比他小的内部节点,就灌水到等高
// 元素出队,入队内部节点,标记visited
int res = 0;
while (!queue.isEmpty()) {
int[] cur = queue.poll(); // 最小节点出队
int[] dir = {-1, 0, 1, 0, -1};
// 循环判断其上下左右四个相邻节点: (-1,0)左,(0,1)上,(1,0)右,(0,-1)下
for (int i = 0; i < 4; i++) {
int nx = cur[0] + dir[i];
int ny = cur[1] + dir[i + 1];
// 判断是否为内部部节点,且没被访问过
if (nx >= 0 && nx < row && ny >= 0 && ny < col && !visited[nx][ny]) {
int temp = cur[2] - heightMap[nx][ny];
// 为内部节点,且比当前节点(外部节点)低,则可以蓄水
if(temp > 0){
res += temp;
}
// 将可以蓄水的节点作为外部节点入队,并标记为已经访问过
queue.offer(new int[]{nx, ny, Math.max(heightMap[nx][ny], cur[2])});
visited[nx][ny] = true;
}
}
}
return res;
}
}
总结
学而时习之,不亦乐乎。
每日一更,敬请期待。