【算法题】多源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中记录的最大值;
相关推荐
TracyCoder1232 小时前
LeetCode Hot100(18/100)——160. 相交链表
算法·leetcode
浒畔居2 小时前
泛型编程与STL设计思想
开发语言·c++·算法
独处东汉3 小时前
freertos开发空气检测仪之输入子系统结构体设计
数据结构·人工智能·stm32·单片机·嵌入式硬件·算法
乐迪信息3 小时前
乐迪信息:AI防爆摄像机在船舶监控的应用
大数据·网络·人工智能·算法·无人机
放荡不羁的野指针3 小时前
leetcode150题-滑动窗口
数据结构·算法·leetcode
小龙报3 小时前
【C语言进阶数据结构与算法】单链表综合练习:1.删除链表中等于给定值 val 的所有节点 2.反转链表 3.链表中间节点
c语言·开发语言·数据结构·c++·算法·链表·visual studio
TracyCoder1234 小时前
LeetCode Hot100(13/100)——238. 除了自身以外数组的乘积
算法·leetcode
CoderCodingNo4 小时前
【GESP】C++五级练习题 luogu-P3353 在你窗外闪耀的星星
开发语言·c++·算法
Anastasiozzzz4 小时前
LeetCode Hot100 215. 数组中的第K个最大元素
数据结构·算法·leetcode