417. 太平洋大西洋水流问题
思路:题目:
Given the following 5x5 matrix:
Pacific ~ ~ ~ ~ ~
~ 1 2 2 3 (5) *
~ 3 2 3 (4) (4) *
~ 2 4 (5) 3 1 *
~ (6) (7) 1 4 5 *
~ (5) 1 1 2 4 *
* * * * * Atlantic
Return:
[[0, 4], [1, 3], [1, 4], [2, 2], [3, 0], [3, 1], [4, 0]] (positions with parentheses in above matrix).
左边和上边是太平洋,右边和下边是大西洋,内部的数字代表海拔,海拔高的地方的水能够流到低的地方,求解水能够流到太平洋和大西洋的所有位置。
显然这种题目用DFS或者BFS都是可以的,这并不是求最短路径的问题,并且DFS可以使代码更加简洁清晰,所以我们使用DFS来解题。->
这一题的难点不在于如何进行递归,如果矩阵的四周都是大西洋或者太平洋就好说,但是消灾的情况并不是这样,我们要求的是水能够流到太平洋和大西洋的所有位置。所以我们要用两个boolean矩阵分别记录能流向大西洋和太平洋的左边取“与”即可。->
由于只有最高点才可能成为所求点,从最高点往周围遍历显然没有直接从四周往中间遍历来得清晰(因为四周的点数更少):
int m = matrix.length;
if(m < 1) return ret;
int n = matrix[0].length;
boolean[][] Pacific = new boolean[m][n];
boolean[][] Atlantic = new boolean[m][n];
for(int i = 0; i < m; ++i) {
dfs(matrix, i, 0, Pacific, matrix[i][0]);
dfs(matrix, i, n-1, Atlantic, matrix[i][n-1]);
}
for(int i = 0; i < n; ++i) {
dfs(matrix, 0, i, Pacific, matrix[0][i]);
dfs(matrix, m-1, i, Atlantic, matrix[m-1][i]);
}
接下来来构造DFS递归函数,首先我们需要确定递归函数的出口,即超出边界或者已访问过,或者不满足水的流向条件:
if(x < 0 || y < 0 || x >= m.length || y >= m[0].length || visited[x][y] || m[x][y] < pre)
return;
接下来向四周进行递归,并将visited置为已访问:
visited[x][y] = true;
dfs(m, x+1, y, visited, m[x][y]);
dfs(m, x-1, y, visited, m[x][y]);
dfs(m, x, y+1, visited, m[x][y]);
dfs(m, x, y-1, visited, m[x][y]);
最后完整的遍历一遍数组,满足Pacific[i][j] && Atlantic[i][j]的即为所求点,将其加入到结果集中,完整代码如下:
public List<List<Integer>> pacificAtlantic(int[][] matrix) {
List<List<Integer>> ret = new ArrayList<>();
int m = matrix.length;
if(m < 1) return ret;
int n = matrix[0].length;
boolean[][] Pacific = new boolean[m][n];
boolean[][] Atlantic = new boolean[m][n];
for(int i = 0; i < m; ++i) {
dfs(matrix, i, 0, Pacific, matrix[i][0]);
dfs(matrix, i, n-1, Atlantic, matrix[i][n-1]);
}
for(int i = 0; i < n; ++i) {
dfs(matrix, 0, i, Pacific, matrix[0][i]);
dfs(matrix, m-1, i, Atlantic, matrix[m-1][i]);
}
for(int i = 0; i < m; ++i) {
for(int j = 0; j < n; ++j)
if(Pacific[i][j] && Atlantic[i][j]){
List<Integer> list = new ArrayList<>();
list.add(i);
list.add(j);
ret.add(list);
}
}
return ret;
}
其中递归函数如下:
public void dfs(int[][] m, int x, int y, boolean[][] visited, int pre) {
if(x < 0 || y < 0 || x >= m.length || y >= m[0].length || visited[x][y] || m[x][y] < pre)
return;
visited[x][y] = true;
dfs(m, x+1, y, visited, m[x][y]);
dfs(m, x-1, y, visited, m[x][y]);
dfs(m, x, y+1, visited, m[x][y]);
dfs(m, x, y-1, visited, m[x][y]);
}