算法系列--多源BFS问题

💕"对相爱的人来说,对方的心意,才是最好的房子。"💕

作者:Lvzi

文章主要内容:算法系列--多源BFS问题

大家好,今天为大家带来的是算法系列--多源BFS问题

前言:

之前我们已经学习过单源的最短路问题,核心就是通过bfs实现来记录最短的路径,解题步骤如下:

  1. 将起点添加进队列--q.add(start)
  2. 一层一层往外扩展(step++)

所谓的多源最短路问题就是有多个起点的最短路问题,如何解决呢?相信大家的第一想法是:不就是多个起点么,每个起点我都做一次单源的最短路问题,再返回结果中最小的不就行了么,这种做法当然是可以的,但是时间复杂度过高,下面讲解第二种方法--超源起点

超源起点就是将所有的起点当做一个起点,以这个点为起点到终点之间的路径就是最短路,从感性上讲为什么这种策略是对的呢?假设我们现在有三个起点,则最短的路径一定是在以这三个点为起点的最短路径之中,则最后求出的一定是最短路径

这样我们就将多源的最短路问题转化为单源的最短路问题!解法同单源最短路问题,下面是leetocode上比较经典的多源bfs问题

一.01 矩阵(medium)

题目链接:01 矩阵(medium)

分析:

转化为从0到1的最短路问题

代码:

java 复制代码
// 正难则反的思想
// 将0看作起点,1看做终点  转化为从0-1的最短路问题
class Solution {
    int[] dx = { 1, -1, 0, 0 };
    int[] dy = { 0, 0, 1, -1 };

    public int[][] updateMatrix(int[][] mat) {
        int m = mat.length, n = mat[0].length;
        int[][] dist = new int[m][n];
        Queue<int[]> q = new LinkedList<>();

        // 将所有起点添加进入队列之中
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                if (mat[i][j] != 0)
                    dist[i][j] = -1;// 将不是起点的位置全部初始化为-1
                if (mat[i][j] == 0)
                    q.add(new int[] { i, j });// 将所有的起点添加进队列之中
            }
        }

        // 多源bfs
        while (!q.isEmpty()) {
            int[] t = q.poll();
            int a = t[0], b = t[1];
            for (int i = 0; i < 4; i++) {
                int x = a + dx[i], y = b + dy[i];
                if (x >= 0 && y >= 0 && x < m && y < n && dist[x][y] == -1) {
                    q.add(new int[] { x, y });
                    dist[x][y] = dist[a][b] + 1;
                }
            }
        }

        return dist;
    }
}

总结:

两个小优化的地方:在单源bfs问题之中,我们经常使用三个变量

  • boolean[][] vis:用于标记是否被搜索过
  • int step:用于记录当前的层数(路径长度)
  • int sz:当前层一共有多少个节点

但是在本题中这三个变量都不存在,实际上是通过其他方式来替代了:

  • 将dist数组中不是起点的位置初始化为-1--替代了vis的作用,在判断时,只需判断dist[x][y]是否等于-1即可,如果等于证明没有被搜索过
  • 由于我们在往外扩展的时候是一步一步往外扩展的,dist[a][b]存储的就是走到当前层所需的最小步数,那么以a,b为起点的下一层的所有位置的步数全部都是dist[a][b] + 1的值,不需要通过step来记录路径长度,且也不需要sz来保证是同一层(只要dist[a][b]相同,就是同一层)

二.⻜地的数量

题目链接:⻜地的数量

分析:

被围绕的区域类似,这里用的是多源bfs

代码:

java 复制代码
// 1.将边界上所有的1添加进入队列
// 2.将所有的联通块(1)标记为0
// 3.统计1的数目,返回即可
class Solution {
    int[] dx = {1,-1,0,0};
    int[] dy = {0,0,1,-1};
    boolean[][] vis;
    public int numEnclaves(int[][] grid) {
        int m = grid.length, n = grid[0].length;
        vis = new boolean[m][n];
        Queue<int[]> q = new LinkedList<>();
		// 将边界上所有的1添加进入队列
        for(int i = 0; i < m; i++) {
            if(grid[i][0] == 1) q.add(new int[]{i,0});
            if(grid[i][n - 1] == 1) q.add(new int[]{i,n - 1});
        }

        for(int j = 0; j < n; j++) {
            if(grid[0][j] == 1) q.add(new int[]{0,j});
            if(grid[m - 1][j] == 1) q.add(new int[]{m - 1, j});
        }

        // 多源bfs  将所有的联通块1标记为0
        while(!q.isEmpty()) {
            int[] t = q.poll();
            int a = t[0], b = t[1];
            grid[a][b] = 0;

            for(int k = 0; k < 4; k++) {
                int x = a + dx[k], y = b + dy[k];
                if(x >= 0 && x < m && y >= 0 && y < n && grid[x][y] == 1) {
                    q.add(new int[]{x, y});
                    grid[x][y] = 0;
                }
            }
        }

        int ret = 0;
        for(int i = 0; i < m; i++) 
            for(int j = 0; j < n; j++)
                if(grid[i][j] == 1) ret++;

        return ret;
    }
}

三.地图中的最⾼点

题目链接:地图中的最⾼点

分析:

多源bfs

代码:

java 复制代码
// 和矩阵那道题目相同
class Solution {
    int[] dx = {1,-1,0,0};
    int[] dy = {0,0,1,-1};
    public int[][] highestPeak(int[][] isWater) {
        int m = isWater.length, n = isWater[0].length;
        int[][] height = new int[m][n];
        Queue<int[]> q = new LinkedList<>();
        for(int i = 0; i < m; i++) {
            for(int j = 0; j < n; j++) {
                if(isWater[i][j] == 1) {
                    q.add(new int[]{i, j});// 将水域添加进队列
                }else {
                    height[i][j] = - 1;// 陆地置为-1
                }
            }
        }

        // 多源bfs
        while(!q.isEmpty()) {
            int[] t = q.poll();
            int a = t[0], b = t[1];
            for(int k = 0; k < 4; k++) {
                int x = a + dx[k], y = b + dy[k];
                if(x >= 0 && x < m && y >= 0 && y < n && height[x][y] == -1) {
                    q.add(new int[]{x, y});
                    height[x][y] = height[a][b] + 1;
                }
            }
        }

        return height;
    }
}

四.地图分析

题目链接:地图分析

分析:

多源bfs + 更新最值

以海洋为起点计算到陆地的最短距离比较困难(无法确定是哪一个海洋)

所以改为以陆地为起点,计算其到海洋的距离

代码:

java 复制代码
class Solution {
    int[] dx = {1,-1,0,0};
    int[] dy = {0,0,1,-1};
    public int maxDistance(int[][] grid) {
        int n = grid.length, ret = 0;
        int[][] dist = new int[n][n];
        Queue<int[]> q = new LinkedList<>();

        boolean flg = false;
        for(int i = 0; i < n; i++) {
            for(int j = 0; j < n; j++) {
                if(grid[i][j] == 1) q.add(new int[]{i, j});// 将陆地当做起点
                else {
                    dist[i][j] = -1;// 将所有海洋置为-1
                    flg = true;// 存在海洋
                }
            }
        }

        if(q.size() == 0) return -1;// 全是海洋
        if(!flg) return -1;// 全是陆地

        // 多源bfs
        while(!q.isEmpty()) {
            int[] t = q.poll();
            int a = t[0], b = t[1];
            for(int k = 0; k < 4; k++) {
                int x = a + dx[k], y = b + dy[k];
                if(x >= 0 && y >= 0 && x < n && y < n && dist[x][y] == -1) {
                    q.add(new int[]{x, y});
                    dist[x][y] = dist[a][b] + 1;
                    ret = Math.max(ret, dist[x][y]);// 更新最值
                }
            }
        }

        return ret;
    }
}

五.总结

  • 多源bfs最常用的一个思想就是正难则反,这个思想主要用于解决谁是起点,谁是终点的问题
  • 多源bfs问题的代码 比较固定
相关推荐
肥猪猪爸22 分钟前
使用卡尔曼滤波器估计pybullet中的机器人位置
数据结构·人工智能·python·算法·机器人·卡尔曼滤波·pybullet
readmancynn34 分钟前
二分基本实现
数据结构·算法
萝卜兽编程36 分钟前
优先级队列
c++·算法
盼海44 分钟前
排序算法(四)--快速排序
数据结构·算法·排序算法
LZXCyrus1 小时前
【杂记】vLLM如何指定GPU单卡/多卡离线推理
人工智能·经验分享·python·深度学习·语言模型·llm·vllm
Enougme1 小时前
Appium常用的使用方法(一)
python·appium
一直学习永不止步1 小时前
LeetCode题练习与总结:最长回文串--409
java·数据结构·算法·leetcode·字符串·贪心·哈希表
懷淰メ1 小时前
PyQt飞机大战游戏(附下载地址)
开发语言·python·qt·游戏·pyqt·游戏开发·pyqt5
hummhumm1 小时前
第 22 章 - Go语言 测试与基准测试
java·大数据·开发语言·前端·python·golang·log4j
hummhumm1 小时前
第 28 章 - Go语言 Web 开发入门
java·开发语言·前端·python·sql·golang·前端框架