💕"对相爱的人来说,对方的心意,才是最好的房子。"💕
作者:Lvzi
文章主要内容:算法系列–多源BFS问题
大家好,今天为大家带来的是
算法系列--多源BFS问题
前言:
之前我们已经学习过单源的最短路问题
,核心就是通过bfs
实现来记录最短的路径,解题步骤如下:
- 将起点添加进队列–q.add(start)
- 一层一层往外扩展(step++)
所谓的多源最短路问题
就是有多个起点的最短路问题,如何解决呢?相信大家的第一想法是:不就是多个起点么,每个起点我都做一次单源的最短路问题,再返回结果中最小的不就行了么,这种做法当然是可以的,但是时间复杂度过高,下面讲解第二种方法–超源起点
超源起点
就是将所有的起点当做一个起点,以这个点为起点到终点之间的路径就是最短路,从感性上讲为什么这种策略是对的呢?假设我们现在有三个起点,则最短的路径一定是在以这三个点为起点的最短路径之中,则最后求出的一定是最短路径
这样我们就将多源的最短路问题转化为单源的最短路问题!解法同单源最短路问题,下面是leetocode上比较经典的多源bfs
问题
一.01 矩阵(medium)
题目链接:
01 矩阵(medium)
分析:
转化为从0到1的最短路问题
代码:
// 正难则反的思想
// 将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;// 将不是起点的位置全部初始化为-1if (mat[i][j] == 0)q.add(new int[] { i, j });// 将所有的起点添加进队列之中}}// 多源bfswhile (!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
代码:
// 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标记为0while(!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
代码:
// 和矩阵那道题目相同
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}}}// 多源bfswhile(!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 + 更新最值
以海洋为起点计算到陆地的最短距离比较困难(无法确定是哪一个海洋)
所以改为以陆地为起点,计算其到海洋的距离
代码:
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;// 将所有海洋置为-1flg = true;// 存在海洋}}}if(q.size() == 0) return -1;// 全是海洋if(!flg) return -1;// 全是陆地// 多源bfswhile(!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问题的代码 比较固定