【算法题】多源BFS

多源BFS将所有满足条件的起点同时入队 (视为"第0层"),再按层扩散,能高效解决"多个源点到网格中各点的最短距离""全局最短/最长距离""边界连通域标记"等问题。其核心优势是:仅需一次遍历即可完成所有源点的扩散,时间复杂度与单源BFS一致(O(mn)O(mn)O(mn)),远优于"对每个点做单源BFS"的暴力解法(O((mn)2)O((mn)^2)O((mn)2))。本文通过4道经典多源BFS题目,拆解多源BFS的核心框架与场景化适配技巧。

一、01矩阵(更新矩阵)

题目描述 :给定一个由 01 组成的矩阵 mat,将每个 1 替换为到最近 0 的最短距离,0 保持不变,返回更新后的矩阵。

核心思路:多源BFS求最短距离

普通单源BFS只能求"一个0"到各点的距离,而本题需要"所有0"到各1的最短距离------将所有 0 作为同时出发的起点 (距离为0),入队后按层扩散,每个 1 第一次被访问时的距离,就是到最近 0 的最短距离(多源扩散保证了"最短")。

代码解析

cpp 复制代码
class Solution {
    int m, n;
    int dx[4] = {0, 0, 1, -1}; // 上下左右方向数组
    int dy[4] = {1, -1, 0, 0};
public:
    vector<vector<int>> updateMatrix(vector<vector<int>>& mat) {
        m = mat.size(), n = mat[0].size();
        vector<vector<int>> dist(m, vector(n, -1)); // 距离数组:-1表示未访问
        queue<pair<int, int>> q;

        // 步骤1:所有0作为起点入队,距离设为0
        for(int i = 0; i < m; i++)
            for(int j = 0; j < n; j++)
                if(mat[i][j] == 0)  
                {
                    dist[i][j] = 0;
                    q.push({i, j});
                }

        // 步骤2:多源BFS扩散
        while(q.size())
        {
            auto [a, b] = q.front();
            q.pop();
            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)
                {
                    dist[x][y] = dist[a][b] + 1; // 邻接节点距离=当前+1
                    q.push({x, y});
                }
            }
        }
        return dist;
    }
};

关键细节

  • dist 数组既记录距离,又充当"访问标记"(-1=未访问),无需额外开vis数组;
  • 多源同时扩散,确保每个1被"最近的0"先访问,距离即为最短。

二、飞地数量(numEnclaves)

题目描述 :给定01矩阵,"飞地"是指无法从边界的 1 到达的内部 1,求飞地的数量。

核心思路:反向多源BFS标记连通域

飞地的本质是"不与边界1连通的内部1"------反向思考:将所有边界的1 作为多源起点,标记所有可达的1,最后统计未被标记的1的数量即为飞地数。

代码解析

cpp 复制代码
class Solution {
    int dx[4] = {0, 0, 1, -1};
    int dy[4] = {1, -1, 0, 0};
public:
    int numEnclaves(vector<vector<int>>& grid) {
        int m = grid.size(), n = grid[0].size();                                                       
        vector<vector<bool>> vis(m, vector<bool>(n)); // 访问标记:是否与边界1连通
        queue<pair<int, int>> q;

        // 步骤1:所有边界的1作为起点入队并标记
        for(int i = 0; i < m; i++)
            for(int j = 0; j < n; j++)
                if(i == 0 || j == 0 || i == m - 1 || j == n - 1) // 边界坐标
                {
                    if(grid[i][j] == 1)
                    {
                        q.push({i, j});
                        vis[i][j] = true;
                    }
                }

        // 步骤2:多源BFS标记所有可达的1
        while(q.size())
        {
            auto [a, b] = q.front();
            q.pop();
            for(int i = 0; i < 4; i++)
            {
                int x = a + dx[i], y = b + dy[i];
                // 边界检查 + 未标记 + 是1
                if(x >= 0 && y >= 0 && x < m && y < n && !vis[x][y] && grid[x][y] == 1)
                {
                    vis[x][y] = true;
                    q.push({x, y});
                }
            }
        }

        // 步骤3:统计未被标记的1(飞地)
        int ret = 0;
        for(int i = 0; i < m; i++)
            for(int j = 0; j < n; j++)
                if(!vis[i][j] && grid[i][j] == 1)
                    ret++;
        return ret;
    }
};

关键细节

  • 反向多源的核心是"标记不需要的部分",剩余未标记的即为目标;
  • 仅遍历边界坐标作为起点,避免无效遍历。

三、最高海拔(highestPeak)

题目描述 :给定矩阵 isWater(1=水域,0=陆地),要求为每个陆地分配海拔:

  1. 水域海拔为0;
  2. 相邻单元格海拔差≤1;
  3. 海拔尽可能大。

核心思路:多源BFS求"最远最近距离"

满足"相邻差≤1且海拔最大"的唯一解是:每个陆地的海拔 = 到最近水域的最短距离(多源BFS的天然结果)。这道题是"01矩阵"的语义变体,逻辑完全一致。

代码解析

cpp 复制代码
class Solution {
    int dx[4] = {0, 0, 1, -1};
    int dy[4] = {1, -1, 0, 0};
public:
    vector<vector<int>> highestPeak(vector<vector<int>>& isWater) {
        int m = isWater.size(), n = isWater[0].size();
        vector<vector<int>> dist(m, vector(n, -1)); // dist数组存储海拔
        queue<pair<int, int>> q;

        // 步骤1:所有水域(1)作为起点,海拔设为0
        for(int i = 0; i < m; i++)
            for(int j = 0; j < n; j++)
                if(isWater[i][j] == 1)
                {
                    dist[i][j] = 0;
                    q.push({i, j});
                }

        // 步骤2:多源BFS扩散,海拔=最近水域距离+1
        while(q.size())
        {
            auto [a, b] = q.front(); q.pop();
            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)
                {
                    dist[x][y] = dist[a][b] + 1;
                    q.push({x, y});
                }
            }
        }
        return dist;
    }
};

关键细节

  • 语义转换:"海拔"="到最近水域的最短距离",完全复用01矩阵的多源BFS逻辑;
  • 无需额外条件,多源扩散的结果天然满足"相邻差≤1"和"海拔最大"。

四、海洋陆地最远距离(maxDistance)

题目描述 :给定01矩阵(1=陆地,0=海洋),求海洋到最近陆地的最远距离;若全是陆地/全是海洋,返回-1。

核心思路:多源BFS求全局最大最短距离

所有陆地作为多源起点,计算每个海洋到最近陆地的最短距离,最后取这些距离的最大值即为答案。

代码解析

cpp 复制代码
class Solution {
    int dx[4] = {0, 0, 1, -1};
    int dy[4] = {1, -1, 0, 0};
public:
    int maxDistance(vector<vector<int>>& grid) {
        int m = grid.size(), n = grid[0].size();
        vector<vector<int>> dist(m, vector(n, -1));
        queue<pair<int, int>> q;

        // 步骤1:所有陆地(1)作为起点入队,距离设为0
        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({i, j});
                }

        // 步骤2:多源BFS计算最短距离,同时记录最大值
        int ret = -1;
        while(q.size())
        {
            auto [a, b] = q.front(); q.pop();
            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)
                {
                    dist[x][y] = dist[a][b] + 1;
                    q.push({x, y});
                    ret = max(ret, dist[x][y]); // 更新最大距离
                }
            }
        }
        return ret; // 全陆地时ret=-1,全海洋时也为-1,符合要求
    }
};

关键细节

  • 无需额外遍历矩阵找最大值,在BFS过程中实时更新即可;
  • 边界条件自然满足:全陆地时没有海洋被访问,ret保持-1;全海洋时q初始为空,ret也为-1。

多源BFS通用框架总结

cpp 复制代码
// 通用框架
1. 初始化:
   - 距离/访问数组(dist/vis):-1/False 表示未访问;
   - 队列q,将所有满足条件的多源起点入队,并初始化dist/vis;

2. 多源扩散:
   while(队列非空) {
       取出队首节点(a,b);
       遍历四个方向:
           计算邻接节点(x,y);
           if(边界合法 && 未访问) {
               更新dist/vis = 当前节点值 + 1 / True;
               入队(x,y);
               (可选)更新目标值(如最大距离、计数等);
           }
   }

3. 结果计算:
   - 最短距离类:直接返回dist数组;
   - 连通域标记类:统计未标记的节点数;
   - 最大距离类:返回BFS中记录的最大值;
相关推荐
2401_857918292 分钟前
实时数据处理中的C++应用
开发语言·c++·算法
2401_884563242 分钟前
C++中的装饰器模式实战
开发语言·c++·算法
MicroTech20257 分钟前
微算法科技(NASDAQ :MLGO)抗量子区块链技术:筑牢量子时代的数字安全防线
科技·算法·区块链
Ivanqhz9 分钟前
图着色寄存器分配算法(Graph Coloring)
开发语言·javascript·python·算法·蓝桥杯·rust
Elsa️74611 分钟前
洛谷p5718 复习下快速排序和堆排序
数据结构·算法·排序算法
Frostnova丶13 分钟前
LeetCode 3567.子矩阵的最小绝对差
算法·leetcode·矩阵
夏日听雨眠15 分钟前
文件学习9
数据结构·学习·算法
华农DrLai15 分钟前
什么是自动Prompt优化?为什么需要算法来寻找最佳提示词?
人工智能·算法·llm·nlp·prompt·llama
黎阳之光16 分钟前
十五五智赋新程 黎阳之光以AI硬核技术筑造产业数智底座
大数据·人工智能·算法·安全·数字孪生
2401_8914821717 分钟前
C++中的原型模式
开发语言·c++·算法