01 矩阵——BFS广度优先搜索图解

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

注意:

  1. 给定矩阵的元素个数不超过 10000。
  2. 给定矩阵中至少有一个元素是 0。
  3. 矩阵中的元素只在四个方向上相邻: 上、下、左、右。

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_VALUE10000也行,这里只是为了标记这个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();
		}
    }

参考资料

sweetiee

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值