Analysis
It's hard to start from inside the 2-D graph and find out the boarder of a crater. But if we start from the boarder and traverse into the graph from the smallest height of the boarders, we know we can trap some water once there is some height less than current height.
This is easy to solve with a minimum heap, since we only want to take out the smallest height among all boarders we have encountered.
So the algorithm is like following:
- store all of the boarders around the graph into a minimum heap. keep a 2-d boolean array visited to record the pointes already being visited.
- take out the smallest height and start searching its neighbors in 4 directions. If the point is not visited, consider following conditions:
- if the height of this point is less than current height, then this point can hold water of the size
(curr.height - thispoint.height)
, since current height is the smallest one among all the boarders and each point have a floor space of 1. After that, since this point is already filled with water, we should upgrade its height to current point's height. - if the height of this point is greater than current height, then no water can be hold by this point, add this new point as a boarder into the minumum heap.
- if the height of this point is less than current height, then this point can hold water of the size
- loop until the minimum heap is empty.
Time and space complexity:
Heapify: O(m + n)
time.
Then every point is put in and take out of the heap at most once. At the same time, we are doing so in a BFS style, meaning that there is m + n cells in the heap in the worst case. So the total time is O(m + n) + O(m * n * (log(m + n)) = O(m * n * (log(m + n))
.
The space complexity would be (m * n)
because of the visited array.
Implementation in Java
public class Solution {
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;
}
}
public int trapRainWater(int[][] heightMap) {
if (heightMap == null || heightMap.length == 0 || heightMap[0].length == 0) {
return 0;
}
Queue<Cell> heap = new PriorityQueue<Cell>(1, new Comparator<Cell>(){
@Override
public int compare(Cell a, Cell b) {
return a.height - b.height;
}
});
int ret = 0;
int m = heightMap.length;
int n = heightMap[0].length;
boolean[][] visited = new boolean[m][n];
for (int i = 0; i < m; i++) {
visited[i][0] = true;
visited[i][n - 1] = true;
heap.offer(new Cell(i, 0, heightMap[i][0]));
heap.offer(new Cell(i, n - 1, heightMap[i][n - 1]));
}
for (int i = 1; i < n - 1; i++) {
visited[0][i] = true;
visited[m - 1][i] = true;
heap.offer(new Cell(0, i, heightMap[0][i]));
heap.offer(new Cell(m - 1, i, heightMap[m - 1][i]));
}
int[][] dirs = new int[][]{{0, 1}, {0, -1}, {1, 0}, {-1, 0}};
while (!heap.isEmpty()) {
Cell curr = heap.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;
ret += Math.max(0, curr.height - heightMap[x][y]);
heap.offer(new Cell(x, y, Math.max(curr.height, heightMap[x][y])));
}
}
}
return ret;
}
}