【算法】广度优先搜索(BFS)

22 篇文章 2 订阅

  昨天学习的深度优先搜索是走到岔路口,选择一条路走到头才返回,然后选择其他岔路继续前进。而广度优先搜索则是走到岔路口后,先按顺序访问当前岔路口能直接到达的所有节点,然后再按访问节点的顺序去访问它们能直接到达的节点。由于是按层次遍历的,所以可以解决一些最短路径的问题。而深度优先搜索可以解决一些求所有解的问题。

  BFS 一般借助队列来实现,下面给出 BFS 的模板写法:

 private void bfs() {
     // 定义队列
     Queue<Object> queue = new LinkedList<>();
     // 起点入队
     queue.offer();
     // 开始遍历
     while (!queue.isEmpty()) {
     	// 节点出队
		int[] pos = queue.poll();
		// 进行处理
     	// 下层节点入队
         queue.offer();
     }
}

下面通过几个题来练习。

1.力扣529.扫雷游戏[1]

让我们一起来玩扫雷游戏!

给你一个大小为 m x n 二维字符矩阵 board ,表示扫雷游戏的盘面,其中:

‘M’ 代表一个 未挖出的 地雷,
‘E’ 代表一个 未挖出的 空方块,
‘B’ 代表没有相邻(上,下,左,右,和所有4个对角线)地雷的 已挖出的 空白方块,
数字(‘1’ 到 ‘8’)表示有多少地雷与这块 已挖出的 方块相邻,
‘X’ 则表示一个 已挖出的 地雷。
给你一个整数数组 click ,其中 click = [clickr, clickc] 表示在所有 未挖出的 方块(‘M’ 或者 ‘E’)中的下一个点击位置(clickr 是行下标,clickc 是列下标)。

根据以下规则,返回相应位置被点击后对应的盘面:

  1. 如果一个地雷(‘M’)被挖出,游戏就结束了- 把它改为 ‘X’ 。

  2. 如果一个 没有相邻地雷 的空方块(‘E’)被挖出,修改它为(‘B’),并且所有和其相邻的 未挖出 方块都应该被递归地揭露。

  3. 如果一个 至少与一个地雷相邻 的空方块(‘E’)被挖出,修改它为数字(‘1’ 到 ‘8’ ),表示相邻地雷的数量。

  4. 如果在此次点击中,若无更多方块可被揭露,则返回盘面。

示例 1:
在这里插入图片描述

输入:board = [["E","E","E","E","E"],["E","E","M","E","E"],["E","E","E","E","E"],["E","E","E","E","E"]], click = [3,0]
输出:[["B","1","E","1","B"],["B","1","M","1","B"],["B","1","1","1","B"],["B","B","B","B","B"]]

示例 2:

输入:board = [["B","1","E","1","B"],["B","1","M","1","B"],["B","1","1","1","B"],["B","B","B","B","B"]], click = [1,2]
输出:[["B","1","E","1","B"],["B","1","X","1","B"],["B","1","1","1","B"],["B","B","B","B","B"]]

这道题的规则很清楚,按照规则来做即可:

  1. 如果初始位置(click)是雷(M),那么直接结束游戏。
  2. 如果是空格(周围没有雷),那么遍历周围节点,把空格(周围一个雷都没有)和数字(周围有雷)挖掘出来。
  3. 如果是数字(周围有雷),那么止步于此。

具体解法如下:

class Solution {
    private int m, n;
    private boolean[][] visited;
    private int[] dx = new int[]{-1, -1, -1, 0, 1, 1, 1, 0};
    private int[] dy = new int[]{-1, 0, 1, 1, 1, 0, -1, -1};
    public char[][] updateBoard(char[][] board, int[] click) {
        m = board.length;
        n = board[0].length;
        visited = new boolean[m][n];
        int x = click[0], y = click[1];
        if (board[x][y] == 'M') {
            board[x][y] = 'X';
        } else {
            bfs(board, x, y);
        }
        return board;
    }
    private void bfs(char[][] board, int posX, int posY) {
        Queue<int[]> queue = new LinkedList<>();
        queue.offer(new int[]{posX, posY});
        visited[posX][posY] = true;
        while (!queue.isEmpty()) {
            int[] pos = queue.poll();

            // 计算周围的炸弹数
            int cnt = 0, x = pos[0], y = pos[1];
            for (int i = 0; i < 8; i++) {
                int nx = x + dx[i];
                int ny = y + dy[i];
                if (nx < 0 || nx >= m || ny < 0 || ny >= n) continue;
                if (board[nx][ny] == 'M') cnt++;
            }

            // 根据结果修改,空格继续,数字到此为止
            if (cnt == 0) {     // 周围没有炸弹,继续挖掘直到发现炸弹和数字
                board[x][y] = 'B';
                for (int i = 0; i < 8; i++) {
                    int nx = x + dx[i];
                    int ny = y + dy[i];
                    if (nx < 0 || nx >= m || ny < 0 || ny >= n || visited[nx][ny] || board[nx][ny] != 'E') continue;
                    queue.offer(new int[]{nx, ny});
                    visited[nx][ny] = true;
                }
            } else {        // 周围有炸弹,记录个数即可
                board[x][y] = (char)(cnt + '0');
            }
        }
    }
}
2.力扣1091. 二进制矩阵中的最短路径[2]

给你一个 n x n 的二进制矩阵 grid 中,返回矩阵中最短 畅通路径 的长度。如果不存在这样的路径,返回 -1 。

二进制矩阵中的 畅通路径 是一条从 左上角 单元格(即,(0, 0))到 右下角 单元格(即,(n - 1, n - 1))的路径,该路径同时满足下述要求:

  • 路径途经的所有单元格都的值都是 0 。

  • 路径中所有相邻的单元格应当在 8 个方向之一 上连通(即,相邻两单元之间彼此不同且共享一条边或者一个角)。

畅通路径的长度 是该路径途经的单元格总数。

示例 1:

在这里插入图片描述

输入:grid = [[0,1],[1,0]]
输出:2

示例 2:

在这里插入图片描述

输入:grid = [[0,0,0],[1,1,0],[1,1,0]]
输出:4

示例 3:

输入:grid = [[1,0,0],[1,1,0],[1,1,0]]
输出:-1

典型的 BFS 求最短路径题目,可以仔细体会这类问题的解法。做法和上面的题类似,也是搜索八个方向,套用 BFS 的模板就行:

class Solution {
    public int shortestPathBinaryMatrix(int[][] grid) {
        int[][] dir = new int[][]{{-1,0},{1,0},{0,-1},{0,1},{-1,-1},{-1,1},{1,1},{1,-1}};
		int n = grid.length;
        if(grid[0][0] == 1) return -1;	// 起点就不通,直接返回
        if(n == 1) return 1;	// 只有起点一个点
        boolean[][] vis = new boolean[n][n];
        Queue<int[]> queue = new LinkedList<>();
        queue.offer(new int[]{0, 0});
        vis[0][0] = true;
        int result = 1;
        while (!queue.isEmpty()) {
            int size = queue.size();	// 当前层有几个点
            for (int i = 0; i < size; i++) {
                int[] pos = queue.poll();
                int x = pos[0];
                int y = pos[1];
                for (int k = 0; k < 8; k++) {
                    int nx = x + dir[k][0];
                    int ny = y + dir[k][1];
                    if (nx >= 0 && nx < n && ny >= 0 && ny < n && grid[nx][ny] == 0 && !vis[nx][ny]) {
                        if (nx == n-1 && ny == n-1) return result + 1;		// 到达了终点
                        queue.offer(new int[]{nx, ny});		// 下层节点入队
                        vis[nx][ny] = true;
                    }
                }
            }
            result++;   // queue 全部弹出,表示当前层已经全部访问,层数加一
        }
        return -1;
    }
}
Reference

[1]力扣529.扫雷游戏:https://leetcode-cn.com/problems/minesweeper

[2]力扣1091. 二进制矩阵中的最短路径:https://leetcode-cn.com/problems/shortest-path-in-binary-matrix/

欢迎关注公众号。
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值