Bootstrap

【搜索回溯算法篇】:多源BFS--从简单BFS到多点协同,探索搜索算法的进化

✨感谢您阅读本篇文章,文章内容是个人学习笔记的整理,如果哪里有误的话还请您指正噢✨
✨ 个人主页:余辉zmh–CSDN博客
✨ 文章所属专栏:搜索回溯算法篇–CSDN博客

在这里插入图片描述

一.什么是多源BFS

多源BFS(Breadth-First Search,广度优先搜索)是BFS的一种扩展形式,用于从多个起点同时开始搜索。与传统的BFS只能从一个起点出发不同,多源BFS允许从多个起点同时进行搜索,适用于需要从多个位置同时探索的场景。

在我的上一篇文章中,用关于使用BFS解决最短路问题的讲解,在这类问题中其实就可以理解为单源BFS搜索,也就是只从一个起始位置开始搜索,而本篇文章则是探讨多个起始位置的最短路问题,使用多源BFS来解。因此在学习这个模块时建议先把单源BFS解决最短路问题理解清楚,才能更好的学习多源BFS。

1.多源BFS的特点

  • 多个起点

    在初始化队列时,将所有起点同时加入到队列中。

  • 同步扩展

    从所有起点同时开始扩展,逐步向外扩展。

  • 层次遍历

    与单源BFS一样,按照层次遍历,确保最先找到的路径是最短的。

2.实现步骤

  • 1.初始化队列

    将所有起始节点加入到队列中。

  • 2.标记访问:

    标记所有起点为已访问。

  • 3.层次遍历:

    从队列中依次取出节点,扩展其相邻节点,如果未访问则加入到队列中。

  • 4.循环步骤3:

    直到队列为空或者找到目标。

和单源BFS的实现步骤除了1不同,需要将多个起始节点入队外,其他步骤完全相同。

3.应用场景

  • 多目标最短路径:

    当需要从多个起点找到某个目标的最短路径时。

  • 地图填充:

    如floodfill算法,从多个点同时开始填充。

  • 多机器人路径规划:

    多个机器人同时从不同起点出发,寻找目标或者覆盖区域。

以最短路径为例讲解,如图所示:

在这里插入图片描述

二.例题

1.01矩阵

题目

在这里插入图片描述

在这里插入图片描述

算法原理

首先先明白本道题的题意是什么,给定一个原始数组,数组中只有0和1两种数字,将所有值为1的位置修改成离当前位置最近的那个0的距离,也就是从1搜索找到离他最近的0,但是仔细分析就会发现,如果从当前的1位置出发可能会遇到好几个0的位置,如何判断哪一个是最近的0呢?

这里就要使用另一个思想,正难则反思想,如果从1去找最近的0不好找,可以反着来,从0去找1,将0所有可以搜索的1修改成对应的距离,因为原始数组中可能存在多个0的位置,这里就要用到多源BFS搜索,多个0同时向外扩展搜索1,相邻位置的值是当前位置的值+1。

在这里插入图片描述

代码实现

typedef pair<int, int> PII;
int dx[4] = {0, 0, 1, -1};
int dy[4] = {1, -1, 0, 0};

vector<vector<int>> updateMatrix(vector<vector<int>>& mat){
    int m = mat.size(), n = mat[0].size();
    //建立一个新的二维数组作为结果返回
    vector<vector<int>> ret(m, vector<int>(n));

    //创建一个队列
    queue<PII> q;

    //找到原始数组中的所有值为0的位置,并在新的数组中修改,同时入队
    for (int i = 0; i < m; i++){
        for (int j = 0; j < n; j++){
            if(mat[i][j]==0){
                ret[i][j] = 0;
                q.push({i, j});
            }
            //将原始数组中非0的位置修改成-1
            else{
                ret[i][j] = -1;
            }
        }
    }

    while(!q.empty()){
        auto [a, b] = q.front();
        q.pop();

        for (int k = 0; k < 4; k++){
            int x = a + dx[k], y = b + dy[k];

            if(x>=0&&x<m&&y>=0&&y<n&&ret[x][y]==-1){
                //将相邻位置值为-1的入队,并修改成当前位置的值+1
                q.push({x, y});
                ret[x][y] = ret[a][b] + 1;
            }
        }
    }

    return ret;
}

2.地图中的最高点

题目

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

算法原理

首先理解题意,原始数组中,1表示水域,0表示陆地
返回一个新的数组,每个位置都表示高度,其中水域高度是0,其他相邻位置的高度与当前位置地高度插不超过1
既然要找某个位置的最大高度,利用贪心地思想,每次相邻位置都比当前位置高1

本道题就转换成01矩阵,多源BFS搜索,从所有水域0的位置开始,逐层的向外扩展搜索,相邻位置的值是当前位置的值+1。

代码实现

typedef pair<int, int> PII;
int dx[4] = {0, 0, 1, -1};
int dy[4] = {1, -1, 0, 0};

vector<vector<int>> highestPeak(vector<vector<int>>& isWater){
    int m = isWater.size(), n = isWater[0].size();
    //创建一个新的数组
    vector<vector<int>> ret(m, vector<int>(n));
    //创建一个队列
    queue<PII> q;

    //遍历原始数组,将值为1表示水域的下标入队,同时对应到新数组修改成高度0
    //值为0的表示陆地的下标,对应到新数组中修改成-1,
    for (int i = 0; i < m; i++){
        for (int j = 0; j < n; j++){
            if(isWater[i][j]==1){
                q.push({i, j});
                ret[i][j] = 0;
            }
            else{
                ret[i][j] = -1;
            }
        }
    }

    //循环出队
    while(!q.empty()){
        //获取队头下标,并出队
        auto [a,b]=q.front();
        q.pop();

        for (int k = 0; k < 4; k++){
            int x = a + dx[k], y = b + dy[k];
            if(x>=0&&x<m&&y>=0&&y<n&&ret[x][y]==-1){
                ret[x][y] = ret[a][b] + 1;
                q.push({x, y});
            }
        }
    }

    return ret;
}

3.地图分析

题目

在这里插入图片描述

在这里插入图片描述

算法原理

本道题的思路还是和01矩阵一样,找到距离陆地最远的海洋格,还是正难则反思想,反过来找,从陆地找海洋格,多源BFS搜索,逐层向外扩展。

代码实现

typedef pair<int, int> PII;
int dx[4] = {0, 0, 1, -1};
int dy[4] = {1, -1, 0, 0};

int maxDistance(vector<vector<int>>& grid){
    //还是和01矩阵一样,正难则反,反过来从陆地找海洋
    //原始数组中0表示海洋,1表示陆地

    int m = grid.size(), n = grid[0].size();

    vector<vector<int>> ret(m, vector<int>(n));
    queue<PII> q;

    for (int i = 0; i < m; i++){
        for (int j = 0; j < n; j++){
            if(grid[i][j]==1){
                q.push({i,j});
                ret[i][j] = 0;
            }
            else{
                ret[i][j] = -1;
            }
        }
    }

    if(q.size()==0||q.size()==m*n){
        return -1;
    }

    int maxnum = 0;
    while(!q.empty()){
        //获取队头下标,并出队
        auto [a,b]=q.front();
        q.pop();

        for (int k = 0; k < 4; k++){
            int x = a + dx[k], y = b + dy[k];
            if(x>=0&&x<m&&y>=0&&y<n&&ret[x][y]==-1){
                ret[x][y] = ret[a][b] + 1;
                maxnum = max(maxnum, ret[x][y]);
                q.push({x, y});
            }
        }
    }

    return maxnum;
}

4.飞地的数量

题目

在这里插入图片描述

在这里插入图片描述

算法原理

本道题其实就是上面写的多源BFS应用场景的示例二,利用floodfill算法思想,搜索连通块。

首先来看题意,给定一个二维数组,数组中只有两种值0或者1,而数值为1的有两种连通块,一种是与边界相连,一种是没有相连,全部被0围绕,找到所有没有与边界相连的1,返回总数量。

在这里插入图片描述

代码实现

typedef pair<int, int> PII;
int dx[4] = {0, 0, 1, -1};
int dy[4] = {1, -1, 0, 0};

int numEnclaves(vector<vector<int>>& grid){
    //正难则反思想,找没有与边界相连的飞地数量难,可以反着来找与边界相连的飞地,剩下的就是最终要找的
    //可以使用深度搜索,也可以使用广度搜索,这里使用多源bfs

    int m = grid.size(), n = grid[0].size();

    //创建一个队列
    queue<PII> q;
    //遍历原始数组将边界上所有的1作为起始位置入队,并修改成2
    //第一列和最后一列
    for (int i = 0; i < m; i++){
        if(grid[i][0]==1){
            grid[i][0] = 2;
            q.push({i, 0});
        }
        if(grid[i][n-1]==1){
            grid[i][n - 1] = 2;
            q.push({i, n - 1});
        }
    }

    //第一行和最后一行
    for (int j = 0; j < n; j++){
        if(grid[0][j]==1){
            grid[0][j] = 2;
            q.push({0, j});
        }
        if(grid[m-1][j]==1){
            grid[m - 1][j] = 2;
            q.push({m - 1, j});
        }
    }

    while(!q.empty()){
        //获取队头元素
        auto [a, b] = q.front();
        q.pop();

        for (int k = 0; k < 4; k++){
            int x = a + dx[k], y = b + dy[k];
            if(x>=0&&x<m&&y>=0&&y<n&&grid[x][y]==1){
                //将所有与边界相连的1都修改成2
                grid[x][y] = 2;
                //入队
                q.push({x, y});
            }
        }
    }

    //遍历整个数组,遇到2说明是与边界相连的,遇到1说明是剩下的飞地,个数相加即可
    int ret=0;
    for(auto nums : grid){
        for(auto num : nums){
            if(num==1){
                ret++;
            }
        }
    }

    return ret;
}

以上就是关于多源BFS练习题的讲解,如果哪里有错的话,可以在评论区指正,也欢迎大家一起讨论学习,如果对你的学习有帮助的话,点点赞关注支持一下吧!!!
在这里插入图片描述

;