单源BFS
通常我们使用 BFS 求最短路,都是针对如下场景:从特定的起点出发,求解到达特定终点的最短距离。
多源BFS
与「单源最短路」不同,「多源最短路」问题是求从「多个源点」到达「一个/多个汇点」的最短路径。
在实现上,最核心的搜索部分,「多源 BFS」与「单源 BFS」并无区别。
多源BFS应用题(leetcode):
1162.地图分析
994.腐烂的橘子
542.01矩阵
1765.地图中的最高点
这几道都是多源BFS的板子题或稍稍改变一些应用
举例:
题目要求让矩阵中的最高高度最大,我们可以通过最大化每个格子的高度来做到这一点。由于任意相邻的格子高度差至多为 1,这意味着对于每个格子,其高度至多比其相邻格子中的最小高度多 1。
题目要求水域的高度必须为 0,因此水域的高度是已经确定的值,我们可以从水域出发,推导出其余格子的高度:
- 首先,计算与水域相邻的格子的高度。对于这些格子来说,其相邻格子中的最小高度即为水域的高度 0,因此这些格子的高度为 1。
- 然后,计算与高度为 1 的格子相邻的、尚未被计算过的格子的高度。对于这些格子来说,其相邻格子中的最小高度为 1,因此这些格子的高度为 2。
- 以此类推,计算出所有格子的高度。
可以发现,上述过程就是从水域出发,执行广度优先搜索的过程。因此,记录下所有水域的位置,然后执行广度优先搜索,计算出所有陆地格子的高度,即为答案。
class Solution {
public int[][] highestPeak(int[][] isWater) {
int n=isWater.length,m=isWater[0].length;
int res[][]=new int[n][m];
for(int i=0;i<n;i++){
Arrays.fill(res[i],-1);// -1 表示该格子尚未被访问过
}
Queue<int[]> queue=new ArrayDeque<>();
for(int i=0;i<n;i++){
for(int j=0;j<m;j++){
if(isWater[i][j]==1){
queue.add(new int[]{i,j});// 将所有水域入队
res[i][j]=0;
}
}
}
int dirs[][]=new int[][]{{0,-1},{0,1},{1,0},{-1,0}};
while(!queue.isEmpty()){
int[] poll = queue.poll();
int x=poll[0],y=poll[1];
for(int dir[]:dirs){
int nx=poll[0]+dir[0],ny=poll[1]+dir[1];
if(nx>=0&&nx<n&&ny>=0&&ny<m&&res[nx][ny]==-1){
res[nx][ny]=res[x][y]+1;
queue.add(new int[]{nx,ny});
}
}
}
return res;
}
}
单源BFS:
这是一类特殊的「单源最短路」问题:本质是在一个边权为 的图上,求从特定「源点」出发到达特定「汇点」的最短路径。(看到曼哈顿距离就可以想一下是否可以转换成图上的距离)
class Solution {
int n;
int[][] grid;
public int maxDistance(int[][] _grid) {
grid = _grid;
n = grid.length;
int ans = -1;
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
if (grid[i][j] == 0) {
ans = Math.max(ans, bfs(i, j));
}
}
}
return ans;
}
// 单次 BFS:求解海洋位置 (x,y) 最近的陆地距离
int bfs(int x, int y) {
int[][] dirs = new int[][]{{1,0},{-1,0},{0,1},{0,-1}};
Deque<int[]> d = new ArrayDeque<>();
Map<Integer, Integer> map = new HashMap<>();
d.addLast(new int[]{x, y});
map.put(x * n + y, 0);
while (!d.isEmpty()) {
int[] poll = d.pollFirst();
int dx = poll[0], dy = poll[1];
int step = map.get(dx * n + dy);
if (grid[dx][dy] == 1) return step;
for (int[] di : dirs) {
int nx = dx + di[0], ny = dy + di[1];
if (nx < 0 || nx >= n || ny < 0 || ny >= n) continue;
int key = nx * n + ny;
if (map.containsKey(key)) continue;
d.addLast(new int[]{nx, ny});
map.put(key, step + 1);
}
}
return -1;
}
}
多源 BFS
class Solution {
public int maxDistance(int[][] grid) {
int n=grid.length,m=grid[0].length;
Queue<int[]> queue=new ArrayDeque<>();
for(int i=0;i<n;i++){
for(int j=0;j<m;j++){
if(grid[i][j]==1){
queue.add(new int[]{i,j});//所有陆地进队
}
}
}
int step=0;
int ans=-1;
int dirs[][]=new int[][]{{0,-1},{0,1},{1,0},{-1,0}};
while(!queue.isEmpty()){
int size=queue.size(); //每轮搜索都将队列内所有点进行搜索
step++; //每轮搜索则距离陆地距离+1
for(int i=0;i<size;i++){
int[] poll = queue.poll();
int x=poll[0],y=poll[1];
for(int dir[]:dirs){
int nx=x+dir[0],ny=y+dir[1];
if(nx>=0&&nx<n&&ny>=0&&ny<m&&grid[nx][ny]==0){
grid[nx][ny]=step;
queue.add(new int[]{nx,ny});
ans= Math.max(ans,step);
}
}
}
}
return ans;
}
}
两道题的思路其实差不多,只是在每次搜索中,是否对当前队列size全部搜索,其实本质都是一样的,因为不管是否一次全部搜索,搜索都是按照队列的顺序来的;只不过如果涉及到每轮的属性统一增加的时候(或者求时间,每轮时间都对队列内所有点进行操作的时候),就对队列size中所有的点进行操作