目录
算法原理
多源BFS步骤:
1、542. 01 矩阵
2、1020. 飞地的数量
3、1765. 地图中的最高点
4、1162. 地图分析
多源BFS(Breadth-First Search,广度优先搜索)是解决边权为1的多源最短路径问题的有效算法。在这种情况下,我们有多个起点,目标是找到从任何起点到图中任何其他点的最短路径。与传统的单源BFS相比,多源BFS同时从所有的起点开始搜索,这样可以一次性解决多个起点到所有其他点的最短路径问题。
算法原理
多源BFS的核心思想是同时从所有起点开始搜索,逐层向外扩展,直到遍历完所有可达的节点。算法的关键在于维护一个队列,队列中初始化时包含所有的起点。随后,算法按照广度优先的顺序访问节点,并逐步向外扩展。
假设我们有一个5x5的网格,网格中有几个起点(标记为S)和一个目标点(标记为T)。我们希望找到从任何一个起点到达目标点的最短路径。
+---+---+---+---+---+
| S | | | | |
+---+---+---+---+---+
| | X | X | X | |
+---+---+---+---+---+
| | X | S | X | |
+---+---+---+---+---+
| | | | X | |
+---+---+---+---+---+
| | | T | | |
+---+---+---+---+---+
在这个网格中,X
表示障碍,不能通过。我们的目标是找到从任何一个S到T的最短路径。
多源BFS步骤:
- 初始化队列:将所有起点
S
的坐标放入队列中。 - 开始搜索:从队列中取出一个节点,探索它的上下左右四个方向的邻居节点。
- 扩展邻居节点:如果邻居节点是可达的(即不是
X
),并且未被访问过,则将它们加入队列,并标记为已访问。 - 重复步骤2和3:继续从队列中取出节点并探索,直到队列为空或找到目标点
T
。
通过上面的步骤,我们可以同时从所有起点开始搜索,并在找到目标点时停止。这种方式保证了我们找到的路径是最短的,因为BFS是按层次扩展的,第一次到达目标点的路径一定是最短路径。
想象上面的网格,我们从所有的S
开始,先将所有S
加入队列。然后,我们一层层地向外扩展,直到找到目标点T
。在扩展的过程中,我们绕过了障碍X
,并找到了到达T
的最短路径。
多源BFS的优势在于其简洁性和有效性,特别是当我们需要从多个起点到达目标点时,它能够提供一种直接且高效的解决方案。
1、542. 01 矩阵
思路:多源bfs
在处理由0和1组成的矩阵并计算每个元素到最近的0的距离时,可以采用一种高效的方法:广度优先搜索(BFS)这个问题可以被视为一个多源最短路径问题,其中0的位置是源点。
算法的核心思路是从所有的0开始进行层序遍历(广度优先搜索),而不是从1开始。这样做的好处是可以避免重复遍历同一个元素多次,从而提高效率。具体操作如下:
初始化一个与原矩阵同大小的矩阵
dist
,用于记录到最近0的距离。初始时,将所有元素设置为-1,表示尚未计算距离。对于矩阵中的0元素,其在dist
中的对应位置被设置为0,表示自身到自身的距离为0。将所有0元素的坐标同时加入到一个队列中,视它们为同一层的元素。这样,我们可以同时处理所有的0,以它们为起点向外扩散。
通过队列进行层序遍历。对于队列中的每个元素,检查其上下左右四个方向的邻居。如果某个邻居的距离还未被计算(即
dist
值为-1),则更新该邻居的距离为当前元素的距离加1,并将该邻居坐标加入队列中,以便进一步扩散。继续这个过程,直到队列为空。此时,
dist
矩阵中的每个元素都存储了到最近的0的距离。
class Solution {
public:int dx[4] = {-1, 1, 0, 0}, dy[4] = {0, 0, -1, 1};vector<vector<int>> updateMatrix(vector<vector<int>>& mat) {int m = mat.size(), n = mat[0].size();// dist[i][j] == -1 表⽰:没有搜索过// dist[i][j] != -1 表⽰:最短距离vector<vector<int>> dist(m, vector<int>(n, -1));queue<pair<int, int>> q;for (int i = 0; i < m; i++) {for (int j = 0; j < n; j++) {if (mat[i][j] == 0) {// 值为0的坐标一起入队,属于同一层同时处理(多源)q.push(make_pair(i, j));dist[i][j] = 0;}}}while (q.size()) {auto a = q.front();q.pop();for (int i = 0; i < 4; i++) {int x = dx[i] + a.first, y = dy[i] + a.second;if (x >= 0 && x < m && y >= 0 && y < n && dist[x][y] == -1) {dist[x][y] = dist[a.first][a.second] + 1;q.push(make_pair(x, y));}}}return dist;}
};
2、1020. 飞地的数量
思路:通过广度优先搜索(BFS)来标记所有可以到达边界的陆地单元格,最后统计未被标记的陆地单元格数量。
初始化变量:首先,初始化一个队列
q
和一个vis
(visited)数组来记录已访问的单元格。vis
数组的大小和原始网格grid
一样,用于标记是否已经访问过对应的单元格。边界单元格入队:遍历网格的所有边界单元格(即位于第一行、最后一行、第一列、最后一列的单元格)。对于每一个为陆地(值为1)的边界单元格,将其加入队列
q
中,并在vis
数组中标记为已访问。广度优先搜索:进行BFS遍历。从队列中取出一个单元格,然后检查它的四个相邻单元格(上、下、左、右)。对于每一个未访问过且为陆地的相邻单元格,将其加入队列
q
中,并在vis
数组中标记为已访问。这一过程不断重复,直到队列为空。计数未访问的陆地单元格:经过上述步骤,所有能够到达边界的陆地单元格都被标记为已访问。最后,遍历整个网格,统计所有未被访问过的陆地单元格。这些就是无法到达边界的陆地单元格。
返回结果:返回步骤4中统计的未访问陆地单元格的数量作为结果。
通过这个方法,我们能够有效地找出所有无法通过任意次移动到达边界的陆地单元格,并计算它们的总数。这种方法的关键在于使用BFS来探索和标记所有可以直接或间接到达边界的陆地单元格,从而区分哪些陆地单元格是完全被包围的。
class Solution {
public:int dx[4] = {-1, 1, 0, 0}, dy[4] = {0, 0, -1, 1};int numEnclaves(vector<vector<int>>& grid) {int m = grid.size(), n = grid[0].size();vector<vector<bool>> vis(m, vector<bool>(n, false));queue<pair<int, int>> q;for (int i = 0; i < m; i++) {for (int j = 0; j < n; j++) {if (i == 0 || i == m - 1 || j == 0 || j == n - 1) {if (grid[i][j] == 1) {q.push(make_pair(i, j));vis[i][j] = true;}}}}while (q.size()) {auto a = q.front();q.pop();for (int i = 0; i < 4; i++) {int x = dx[i] + a.first, y = dy[i] + a.second;if (x >= 0 && x < m && y >= 0 && y < n && grid[x][y] == 1 &&!vis[x][y]) {vis[x][y] = true;q.push(make_pair(x, y));}}}int ret = 0;for (int i = 0; i < m; i++) {for (int j = 0; j < n; j++) {if (grid[i][j] == 1 && !vis[i][j])ret++;}}return ret;}
};
3、1765. 地图中的最高点
思路:广度优先搜索是从一组起始点开始(在这个场景中是所有水域格子),然后逐层向外扩散到所有可达的陆地格子,为每个陆地格子分配一个高度值,这个值比其相邻的水域或已处理的陆地格子的高度值大1。
具体步骤如下:
初始化:
- 创建一个与输入矩阵
isWater
大小相同的矩阵dist
,用于存储每个格子的高度。初始时,所有格子的高度设置为-1
(表示未处理)。- 创建一个队列
q
,用于执行广度优先搜索。处理水域格子:
- 遍历整个
isWater
矩阵,找到所有水域格子(isWater[i][j] == 1
的格子)。- 将这些水域格子的高度在
dist
矩阵中设置为0
,并将它们加入队列q
(表示这些格子是搜索的起点)。广度优先搜索:
- 当队列
q
不为空时,从队列中移除一个格子,对于这个格子的每一个相邻的四个方向(北、南、东、西),执行以下步骤:
- 计算相邻格子的坐标
(x, y)
。- 检查
(x, y)
是否在矩阵范围内且该格子的高度是否未被设置(即dist[x][y] == -1
)。- 如果满足条件,将该格子的高度设置为当前格子的高度加1(
dist[x][y] = dist[a.first][a.second] + 1
),然后将(x, y)
加入队列q
。通过以上步骤,每个水域格子周围的陆地格子首先被设置为高度1,然后这些陆地格子的周围陆地格子被设置为高度2,依此类推。这个过程一直持续,直到所有可达的陆地格子都被分配了一个高度值。
最终,
dist
矩阵包含了每个格子的高度,这就是算法的输出。
class Solution {
public:int dx[4] = {-1, 1, 0, 0}, dy[4] = {0, 0, -1, 1};vector<vector<int>> highestPeak(vector<vector<int>>& isWater) {int m = isWater.size(), n = isWater[0].size();vector<vector<int>> dist(m, vector<int>(n, -1));queue<pair<int, int>> q;for (int i = 0; i < m; i++) {for (int j = 0; j < n; j++) {if (isWater[i][j]) {dist[i][j] = 0;q.push(make_pair(i, j));}}}while (q.size()) {auto a = q.front();q.pop();for (int i = 0; i < 4; i++) {int x = a.first + dx[i], y = a.second + dy[i];if (x >= 0 && x < m && y >= 0 && y < n && dist[x][y] == -1) {dist[x][y] = dist[a.first][a.second] + 1;q.push(make_pair(x, y));}}}return dist;}
};
4、1162. 地图分析
思路:多源 bfs直接上手
class Solution {
public:int dx[4] = {-1, 1, 0, 0}, dy[4] = {0, 0, -1, 1};int maxDistance(vector<vector<int>>& grid) {int m = grid.size(), n = grid[0].size();vector<vector<int>> dist(m, vector<int>(n, -1));queue<pair<int, int>> q;for (int i = 0; i < m; i++) {for (int j = 0; j < n; j++) {if (grid[i][j] == 1) {dist[i][j] = 0;q.push(make_pair(i, j));}}}int ret = -1;while (q.size()) {auto a = q.front();q.pop();for (int i = 0; i < 4; i++) {int x = a.first + dx[i], y = a.second + dy[i];if (x >= 0 && x < m && y >= 0 && y < n && dist[x][y] == -1) {dist[x][y] = dist[a.first][a.second] + 1;q.push(make_pair(x, y));ret = max(ret, dist[x][y]);}}}return ret;}
};