542. 01 矩阵
思路:
先说我的思路,我想的是BFS+记忆数组,但是逻辑上出了点问题,先看我的错误代码:
private int[][] vector = new int[][]{{0, 1}, {0, -1}, {1, 0}, {-1, 0}};
public int[][] updateMatrix(int[][] matrix) {
if(matrix.length == 0) return new int[][]{};
int[][] visited = new int[matrix.length][matrix[0].length];
int[][] res = new int[matrix.length][matrix[0].length];
for(int i=0;i<matrix.length;i++){
for(int j=0;j<matrix[0].length;j++){
if(matrix[i][j] == 1){
bfs(matrix,i,j,visited,res);
}
}
}
return res;
}
public void bfs(int[][] matrix,int i,int j,int[][] visited,int[][] res){
//if(visited[i][j] != 0) return visited[i][j];
Queue<int[]> queue = new LinkedList<>();
queue.add(new int[]{i,j});
int level = 0;
while(!queue.isEmpty()){
level++;
int size = queue.size();
for(int p=0;p<size;p++){
int[] s = queue.poll();
int min = Integer.MAX_VALUE;
for (int[] v : vector) {
int r = s[0] + v[0], c = s[1] + v[1];
if(r < 0 || r >= matrix.length
|| c < 0 || c >= matrix[0].length) continue;
if(visited[r][c] != 0){
min = Math.min(min,level + visited[r][c]);
//visited[i][j] = res[i][j];
//return;
}
if (matrix[r][c] == 0) {
min = Math.min(min,level);
//visited[i][j] = level;
//return;
}
if(min == Integer.MAX_VALUE){
queue.add(new int[]{r, c});
}else{
res[i][j] = min;
visited[i][j] = min;
return;
}
}
}
}
输入:
[[0,0,0],[0,1,0],[1,1,1]]
输出:
[[0,0,0],[0,1,0],[1,2,3]]
预期结果:
[[0,0,0],[0,1,0],[1,2,1]]
我的代码看着就挺笨的。。。从测试用例就可以看出,我这个visited数组完全就是鸡肋,不如不要,因为我把这题当作最短路径在计算,其中level + visited[r][c]并不是最短的!要求最短的必须老老实实遍历直到遇到0,所以得去掉visited数组,也就是纯bfs那显然会超时。
所以,需要换个思路,如果我们从0开始遍历呢?那样岂不是能在每次BFS一圈的同时更新多个1的位置?于是,我写成了这个鬼样子:
public int[][] updateMatrix(int[][] matrix) {
int m = matrix.length;
if(m < 1) return matrix;
int n = matrix[0].length;
Queue<int[]> q = new LinkedList<>();
for(int i = 0; i < m; ++i) {
for(int j = 0; j < n; ++j) {
if(matrix[i][j] == 1)
matrix[i][j] = Integer.MAX_VALUE;
else
q.offer(new int[]{i, j});
}
}
int[][] dirs = {{-1,0}, {0,-1}, {1,0}, {0,1}};
int level = 0;
while(!q.isEmpty()) {
int size = q.size();
//System.out.println(size);
level++;
for(int i=0;i<size;i++){
int[] node = q.poll();
for(int[] dir : dirs) {
int x = node[0]+dir[0];
int y = node[1]+dir[1];
if(x < 0 || x >= m || y < 0 || y >= n) continue;
if(matrix[x][y] > 0) matrix[x][y] = Math.min(matrix[x][y],level);
q.offer(new int[]{x, y});
}
}
}
return matrix;
}
连终止条件都达不到,跳不出这个while循环,所以要再加个条件避免重复加入已经遍历过的节点:
public int[][] updateMatrix(int[][] matrix) {
int m = matrix.length;
if(m < 1) return matrix;
int n = matrix[0].length;
Queue<int[]> q = new LinkedList<>();
for(int i = 0; i < m; ++i) {
for(int j = 0; j < n; ++j) {
if(matrix[i][j] == 1)
matrix[i][j] = Integer.MAX_VALUE;
else
q.offer(new int[]{i, j});
}
}
int[][] dirs = {{-1,0}, {0,-1}, {1,0}, {0,1}};
int level = 0;
while(!q.isEmpty()) {
int size = q.size();
//System.out.println(size);
level++;
for(int i=0;i<size;i++){
int[] node = q.poll();
for(int[] dir : dirs) {
int x = node[0]+dir[0];
int y = node[1]+dir[1];
if(x < 0 || x >= m || y < 0 || y >= n || matrix[x][y] <= level) continue;
if(matrix[x][y] > 0) matrix[x][y] = Math.min(matrix[x][y],level);
q.offer(new int[]{x, y});
}
}
}
return matrix;
}
还有人用dfs,我觉得都是可以的,但用dfs时间复杂度可能会更高,还是bfs更容易理解。
当然,这个题目还可以用到动态规划,因为某一个点的离其最近的0相当于周围点离其最近0的最小距离加一,所以我们只需要正着反着各遍历一遍取最小即可:
public int[][] updateMatrix(int[][] matrix) {
int row = matrix.length;
int col = matrix[0].length;
// 第一次遍历,正向遍历,根据相邻左元素和上元素得出当前元素的对应结果
for (int i = 0; i < row; i++) {
for (int j = 0; j < col; j++) {
if (matrix[i][j] == 1) {
matrix[i][j] = Integer.MAX_VALUE;
}
if (i > 0 && matrix[i - 1][j] != Integer.MAX_VALUE) {
matrix[i][j] = Math.min(matrix[i][j], matrix[i - 1][j] + 1);
}
if (j > 0 && matrix[i][j - 1] != Integer.MAX_VALUE) {
matrix[i][j] = Math.min(matrix[i][j], matrix[i][j - 1] + 1);
}
}
}
// 第二次遍历,反向遍历,根据相邻右元素和下元素及当前元素的结果得出最终结果
for (int i = row - 1; i >= 0; i--) {
for (int j = col - 1; j >= 0; j--) {
if (i < row - 1 && matrix[i + 1][j] != Integer.MAX_VALUE) {
matrix[i][j] = Math.min(matrix[i][j], matrix[i + 1][j] + 1);
}
if (j < col - 1 && matrix[i][j + 1] != Integer.MAX_VALUE) {
matrix[i][j] = Math.min(matrix[i][j], matrix[i][j + 1] + 1);
}
}
}
return matrix;
}