01 矩阵——BFS广度优先搜索解答
给定一个由 0 和 1 组成的矩阵,找出每个元素到最近的 0 的距离。
两个相邻元素间的距离为 1 。
示例 1:
输入:
0 0 0
0 1 0
0 0 0
输出:
0 0 0
0 1 0
0 0 0
示例 2:
输入:
0 0 0
0 1 0
1 1 1
输出:
0 0 0
0 1 0
1 2 1
注意:
- 给定矩阵的元素个数不超过 10000。
- 给定矩阵中至少有一个元素是 0。
- 矩阵中的元素只在四个方向上相邻: 上、下、左、右。
BFS广度优先搜索代码
首先把每个源点
0
\mathit{0}
0 入队,然后从各个
0
\mathit{0}
0 同时开始一圈一圈的向
1
\mathit{1}
1 扩散(每个
1
\mathit{1}
1 都是被离它最近的
0
\mathit{0}
0 扩散到的 ),
扩散的时候可以设置 int[][] dist
来记录距离(即扩散的层次)并同时标志是否访问过。
对于本题是可以直接修改原数组 int[][] matrix
来记录距离和标志是否访问的,
这里要注意先把 matrix 数组中 1 的位置设置成 -1。
我们举一个8*8的栗子,初始矩阵
第一步,循环,把0
加入队列,把1
变为-1
(你设置成其它的,如Integer.MAX_VALUE
,10000
也行,这里只是为了标记这个1没有被访问过。)
第二步,
队列弹出元素
,并在该元素
的上下左右
四个方向查找-1
,找到了,
进行运算该元素的数值
+距离1
,把结果填到-1
的位置,
同时 ,把-1
的坐标加入队列,进行下一轮遍历。
下一轮遍历,当前值1
+距离1
=2
下一轮遍历,当前值2
+距离1
=3
下一轮遍历,当前值3
+距离1
=4
class Solution {
public int[][] updateMatrix(int[][] matrix) {
// 首先将所有的 0 都入队,并且将 1 的位置设置成 -1,表示该位置是 未被访问过的 1
Queue<int[]> queue = new LinkedList<>();
int m = matrix.length, n = matrix[0].length;
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
if (matrix[i][j] == 0) {
queue.offer(new int[] {i, j});
} else {
matrix[i][j] = -1;
}
}
}
int[] dx = new int[] {-1, 1, 0, 0};
int[] dy = new int[] {0, 0, -1, 1};
while (!queue.isEmpty()) {
int[] point = queue.poll();
int x = point[0], y = point[1];
for (int i = 0; i < 4; i++) {
int newX = x + dx[i];
int newY = y + dy[i];
// 如果四邻域的点是 -1,表示这个点是未被访问过的 1
// 所以这个点到 0 的距离就可以更新成 matrix[x][y] + 1。
if (newX >= 0 && newX < m && newY >= 0 && newY < n
&& matrix[newX][newY] == -1) {
matrix[newX][newY] = matrix[x][y] + 1;
queue.offer(new int[] {newX, newY});
}
}
}
return matrix;
}
}
这个代码有点难理解,不理解的话,可以看一下下面这个有记录矩阵的。
其它类似代码
题干说找"最近的0的距离",最短路问题第一个想法就是BFS。
找01矩阵中所有元素的距离0的位置:元素0和自身的距离是0,元素1和0的距离等于0到1的距离。
用一个标记数组记录每个位置是否已经计算过距离。
初始化结果集和队列,遍历矩阵找到所有等于0的位置,结果集对应位置赋值0并且坐标入队。计算过距离的位置标记。
广搜,队列中元素出队后向四个方向分别搜索一次寻找1(没有被标记过的位置就是1),如果搜索位置存在1则记录结果集距离为结果集中搜索源点的值+1
,并且入队、标记。
public int[][] updateMatrix(int[][] matrix) {
if (matrix == null || matrix.length == 0) return null;
int m = matrix.length, n = matrix[0].length;
int[][] res = new int[m][n];//结果集
boolean[][] visited = new boolean[m][n];//记录已经计算过的位置
Queue<int[]> queue = new LinkedList<>();//广搜队列
//遍历,将等于0的位置计入结果集并入队
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
if (matrix[i][j] == 0) {
res[i][j] = 0;
visited[i][j] = true;
queue.offer(new int[]{i, j});
}
}
}
//四个方向广搜
int[][] direction = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};//上下左右
while (!queue.isEmpty()) {
int[] poll = queue.poll();
int i = poll[0], j = poll[1];
//四个方向上找 1
for (int k = 0; k < 4; k++) {
int di = i + direction[k][0], dj = j + direction[k][1];
//没有计算过的地方一定是 1
if (di >= 0 && di < m && dj >= 0 && dj < n && !visited[di][dj]) {
res[di][dj] = res[i][j] + 1;
visited[di][dj] = true;
queue.offer(new int[]{di, dj});
}
}
}
return res;
}
测试用例
public static void main(String[] args) {
//int[][] matrix = new int[][] {{0,0,0},{0,1,0},{0,0,0}};
int[][] matrix = new int[][] {{0,0,0},{0,1,0},{1,1,1}};
pirnt(matrix);
System.out.println("===updateMatrix===");
int[][] seen = new Solution().updateMatrix(matrix);
pirnt(seen);
}
public static void pirnt(int[][] seen){
for (int i = 0; i < seen.length; i++) {
for (int j = 0; j < seen[i].length; j++) {
System.out.print(seen[i][j]+"\t");
}
System.out.println();
}
}