多源 BFS 算法详解与经典题目实战
核心思路
单源最短路问题大家很熟悉,只有一个起点到终点的最短路径。但在实际场景中,我们常遇到多源最短路问题:有多个起点,需要计算它们到某个终点(或所有点)的最短距离。
多源 BFS 的核心在于将多个起点视为一个整体。解决这类边权相同的多源最短路问题,通常有两种思路:
- 暴力解法:把多源问题转化为若干个单源问题,分别求解。虽然逻辑简单,但时间复杂度往往较高,容易超时。
- 多源入队:将所有起点同时加入队列,视为同一个'超级起点'开始层序遍历。这样只需一次 BFS 即可覆盖所有情况,效率显著提升。
下面我们通过几道经典题目来具体拆解这种优化思路。
01 矩阵
给定一个由 0 和 1 组成的二维矩阵,要求计算每个元素到最近的 0 的距离。
暴力尝试
如果遍历数组,找到 1 后单独进行 BFS 寻找最近的 0,代码实现不难,但时间复杂度会达到 O(MNM*N),在数据量大时必然超时。
多源优化
换个角度思考:既然要找离 0 最近的距离,不如从所有的 0 出发,向外扩散。第一次扩散到的 1 就是距离为 1,第二次扩散到的就是距离为 2,以此类推。
具体做法是:先将矩阵中所有值为 0 的位置坐标放入队列,并初始化结果数组对应位置为 0。然后进行标准的层序遍历,每次从队列取出一个位置,向四周扩展。如果遇到未访问过的 1,将其标记为已访问,并将距离设为当前节点距离加一,同时入队。
这里有个小细节:我们可以直接用结果数组 ret 充当访问标记。初始时将 ret 全部设为 -1,遇到 0 则设为 0 并入队。BFS 过程中,如果 ret[x][y] 仍为 -1,说明该位置未被访问过,直接更新距离并入队即可。这样省去了额外的 visited 数组空间。
class Solution {
int[] dx = {0, 0, 1, -1};
int[] dy = {1, -1, 0, 0};
public int[][] updateMatrix(int[][] mat) {
int m = mat.length;
int n = mat[0].length;
int[][] ret = new int[m][n];
Queue<int[]> queue = new LinkedList<>();
// 初始化:0 的位置距离为 0,1 的位置暂记为 -1
( ; i < m; i++) {
( ; j < n; j++) {
ret[i][j] = -;
(mat[i][j] == ) {
queue.add( []{i, j});
ret[i][j] = ;
}
}
}
(!queue.isEmpty()) {
[] arr = queue.poll();
( ; i < ; i++) {
arr[] + dx[i];
arr[] + dy[i];
(x >= && x < m && y >= && y < n && ret[x][y] == -) {
queue.add( []{x, y});
ret[x][y] = ret[arr[]][arr[]] + ;
}
}
}
ret;
}
}


