1. 题目
2. 思路
(1) 木桶原理+小顶堆
- 根据木桶原理,木桶中装水的容量取决于最短的木板,因此,下标为[x,y]的方块能够接到雨水的容量取决于上下左右四个方块中接到雨水后高度最小的一个。
- 维护一个小顶堆,存储方块的下标及其接到雨水后的高度。显然,四周的方块必然不能接到雨水,因此,先将四周方块的下标及其高度存入小顶堆中,这样就相当于在小顶堆中存储着一个围栏,不断向内缩小围栏,同时统计雨水的体积,直到围栏内不存在方块。
- 维护一个同等大小的二维数组,标记方块是否被访问过。
- 每次从小顶堆中弹出堆顶元素,访问其上下左右四个方向的方块,若某个方向的方块高度小于堆顶元素接到雨水后的高度,则向该方块中灌水,直到该方块的高度等于堆顶元素的高度,同时累加雨水的体积,然后缩小小顶堆中的围栏。
- 以下图为例,将四周的围栏加入小顶堆后,弹出堆顶元素,如[0,0]的1,由于其四个方向的方块要么处于界外,要么被访问过,因此,不做任何操作,相当于该方块没有任何作用,直接排除即可。以此类推,弹出[0,3]的1,[2,5]的1,[0,5]的2,[2,0]的2,[2,3]的2。
- 此时,小顶堆中维护的围栏如下图所示,再弹堆顶元素,如[0,2]的3,由于其下方的方块高度小于3,因此,向该方块中灌水,使其高度达到3,同时统计雨水的体积2,并将该方块加入小顶堆,以此缩小小顶堆中的围栏。
- 此时,小顶堆中维护的围栏如下图所示,继续按照前面的规则不断弹出堆顶元素,不断缩小围栏,直到堆为空,即可得到接到雨水的总体积。
3. 代码
import java.util.Comparator;
import java.util.PriorityQueue;
public class Test {
public static void main(String[] args) {
}
}
class Solution {
public int trapRainWater(int[][] heightMap) {
int m = heightMap.length;
int n = heightMap[0].length;
PriorityQueue<int[]> queue = new PriorityQueue<>(new Comparator<int[]>() {
@Override
public int compare(int[] o1, int[] o2) {
return o1[2] - o2[2];
}
});
boolean[][] flag = new boolean[m][n];
for (int i = 0; i < m; i++) {
if (i == 0 || i == m - 1) {
for (int j = 0; j < n; j++) {
queue.add(new int[]{i, j, heightMap[i][j]});
flag[i][j] = true;
}
} else {
queue.add(new int[]{i, 0, heightMap[i][0]});
flag[i][0] = true;
queue.add(new int[]{i, n - 1, heightMap[i][n - 1]});
flag[i][n - 1] = true;
}
}
int res = 0;
//上下左右四个方向
int[][] dirs = new int[][]{{0, 1}, {0, -1}, {-1, 0}, {1, 0}};
while (!queue.isEmpty()) {
int[] poll = queue.poll();
int h = poll[2];
for (int[] dir : dirs) {
int x = poll[0] + dir[0];
int y = poll[1] + dir[1];
if (x < 0 || x >= m || y < 0 || y >= n || flag[x][y]) {
continue;
}
if (h > heightMap[x][y]) {
res += (h - heightMap[x][y]);
queue.add(new int[]{x, y, h});
} else {
queue.add(new int[]{x, y, heightMap[x][y]});
}
flag[x][y] = true;
}
}
return res;
}
}