算法套路学习之BFS广度优先搜索

来源:https://leetcode-cn.com/problems/open-the-lock/solution/wo-xie-liao-yi-tao-bfs-suan-fa-kuang-jia-jian-dao-/
又是看这个大佬学习的,下面我自己总结一下,并且记录一下遇到的使用BFS的题目,验证一下这个套路的可行性。一直比较讨厌这种题,因为没有总结出一套框架来。(现在回溯熟悉的差不多了 哈哈哈哈)

一.背景和框架

BFS本质:本质上就是一幅「图」,让你从一个起点,走到终点,问最短路径

下面是算法的框架:

// 计算从起点 start 到终点 target 的最近距离
int BFS(Node start, Node target) {
    Queue<Node> q; // 核心数据结构
    Set<Node> visited; // 避免走回头路
    
    q.offer(start); // 将起点加入队列
    visited.add(start);
    int step = 0; // 记录扩散的步数

    while (q not empty) {
        int sz = q.size();
        /* 将当前队列中的所有节点向四周扩散 */
        for (int i = 0; i < sz; i++) {
            Node cur = q.poll();
            /* 划重点:这里判断是否到达终点 */
            if (cur is target)
                return step;
            /* 将 cur 的相邻节点加入队列 */
            for (Node x : cur.adj())
                if (x not in visited) {
                    q.offer(x);
                    visited.add(x);
                }
        }
        /* 划重点:更新步数在这里 */
        step++;
    }
}


二.例题

1.二叉树的最小高度

LeetCode地址

int BFS(TreeNode root) {
    if (root == null) return 0;
    Queue<TreeNode> q = new LinkedList<>();
    q.offer(root);
    // root 本身就是一层,depth 初始化为 1
    int depth = 1;
    
    while (!q.isEmpty()) {
        int sz = q.size();
        /* 将当前队列中的所有节点向四周扩散 */
        for (int i = 0; i < sz; i++) {
            TreeNode cur = q.poll();
            /* 判断是否到达终点 */
            if (cur.left == null && cur.right == null) 
                return depth;
            /*  cur 的相邻节点加入队列 */
            if (cur.left != null)
                q.offer(cur.left);
            if (cur.right != null) 
                q.offer(cur.right);
        }
        /* 这里增加步数 */
        depth++;
    }
    return depth;
}

解释:

  1. Queue 在Java中用LinkedList<>()来创建实例
  2. 这里不用加vistied记录走过的路径是因为树结构本身
  3. 判断是否达到终点:左子节点和右子节点均为null

2.腐烂的橘子

LeetCode地址

这道题的主要思路是:

  • 一开始,我们找出所有腐烂的橘子,将它们放入队列,作为第 0 层的结点。
  • 然后进行 BFS 遍历,每个结点的相邻结点可能是上、下、左、右四个方向的结点,注意判断结点位于网格边界的特殊情况。
  • 由于可能存在无法被污染的橘子,我们需要记录新鲜橘子的数量。在 BFS 中,每遍历到一个橘子(污染了一个橘子),就将新鲜橘子的数量减一。如果 BFS 结束后这个数量仍未减为零,说明存在无法被污染的橘子。
public int BFS(int[][] grid) {
    int M = grid.length;
    int N = grid[0].length;
    Queue<int[]> queue = new LinkedList<>();//这个T存的其实是腐烂橘子的坐标 跟Node一个道理

    int count = 0; // count 表示新鲜橘子的数量
    for (int r = 0; r < M; r++) {
        for (int c = 0; c < N; c++) {
            if (grid[r][c] == 1) {
                count++;
            } else if (grid[r][c] == 2) {
                queue.add(new int[]{r, c});
            }
        }
    }

    int round = 0; // round 表示腐烂的轮数,或者分钟数
    while (count > 0 && !queue.isEmpty()) {
        int n = queue.size();
        for (int i = 0; i < n; i++) {
            int[] orange = queue.poll();
            int r = orange[0];//横坐标
            int c = orange[1];//纵坐标
            if (r-1 >= 0 && grid[r-1][c] == 1) {
                grid[r-1][c] = 2;
                count--;
                queue.add(new int[]{r-1, c});
            }
            if (r+1 < M && grid[r+1][c] == 1) {
                grid[r+1][c] = 2;
                count--;
                queue.add(new int[]{r+1, c});
            }
            if (c-1 >= 0 && grid[r][c-1] == 1) {
                grid[r][c-1] = 2;
                count--;
                queue.add(new int[]{r, c-1});
            }
            if (c+1 < N && grid[r][c+1] == 1) {
                grid[r][c+1] = 2;
                count--;
                queue.add(new int[]{r, c+1});
            }
        }
        round++;
    }

    if (count > 0) {
        return -1;
    } else {
        return round;
    }
}

3.地图分析

LeetCode地址

代码如下:

public int maxDistance(int[][] grid) {
    int N = grid.length;
    
    Queue<int[]> queue = new LinkedList<>();
    // 将所有的陆地格子加入队列
    for (int i = 0; i < N; i++) {
        for (int j = 0; j < N; j++) {
            if (grid[i][j] == 1) {
                queue.add(new int[]{i, j});
            }
        }
    }
    // 如果我们的地图上只有陆地或者海洋,请返回 -1。
    if (queue.isEmpty() || queue.size() == N * N) {
        return -1;
    }
    
    int distance = -1;
    while (!queue.isEmpty()) {
        distance++;
        int n = queue.size();
        // 这里一口气取出 n 个结点,以实现层序遍历
        for (int i = 0; i < n; i++) {
            int[] cell = queue.poll();
            int r = cell[0];
            int c = cell[1];
            // 遍历上方单元格
            if (r-1 >= 0 && grid[r-1][c] == 0) {
                grid[r-1][c] = 2;
                queue.add(new int[]{r-1, c});
            }
            // 遍历下方单元格
            if (r+1 < N && grid[r+1][c] == 0) {
                grid[r+1][c] = 2;
                queue.add(new int[]{r+1, c});
            }
            // 遍历左边单元格
            if (c-1 >= 0 && grid[r][c-1] == 0) {
                grid[r][c-1] = 2;
                queue.add(new int[]{r, c-1});
            }
            // 遍历右边单元格
            if (c+1 < N && grid[r][c+1] == 0) {
                grid[r][c+1] = 2;
                queue.add(new int[]{r, c+1});
            }
        }
    }
    
    return distance;
}

题目问的是「距离陆地区域最远的海洋区域」,怎么理解呢:

  • 其实就是从陆地(1)开始,要扩散多少次,才能把所有的海洋给覆盖掉。「最远」应该从这个角度来理解。

而「该海洋区域到离它最近的陆地区域的距离」,怎么理解呢:

  • 最近是因为:BFS的搜索都是一层一层的,如果这个陆地是最近的,那么它一定是先扩散到这个海洋。

解释:

  • 一开始,我们找出所有陆地格子,将它们放入队列,作为第 0 层的结点。为什么要找陆地,不把海洋放到队列中呢,因为题目是要找出这个最远海洋的坐标,所以肯定是通过陆地出发去找,所以就把陆地坐标加入到队列
  • 然后进行 BFS 遍历,每个结点的相邻结点可能是上、下、左、右四个方向的结点,注意判断结点位于网格边界的特殊情况。
  • 当遍历结束时,当前的遍历层数就是海洋格子到陆地格子的最远距离。
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值