BFS(广度优先搜索算法)入门

导入

和上次学习DFS一样,我先给出BFS题目的经典模板(如下)

/** 
 * 广度优先搜索 
 * @param Vs 起点 
 * @param Vd 终点 
 */ 
bool BFS(Node& Vs, Node& Vd){  
    queue<node> Q;  
    Node Vn, Vw;  
    int i;  
   
    //初始状态将起点放进队列Q  
    Q.push(Vs);  
    hash(Vw) = true;//设置节点已经访问过了!  
   
    while (!Q.empty()){//队列不为空,继续搜索!  
        //取出队列的头Vn  
        Vn = Q.front();  
   
        //从队列中移除  
        Q.pop();  
   
        while(Vw = Vn通过某规则能够到达的节点){  
            if (Vw == Vd){//找到终点了!  
                //把路径记录,这里没给出解法  
                return true;//返回  
            }  
   
            if (isValid(Vw) && !visit[Vw]){  
                //Vw是一个合法的节点并且未走过
                Q.push(Vw);//加入队列Q  
                hash(Vw) = true;//设置节点走过
            }  
        }  
    }  
    return false;//无解  
}  </node>

下面开始我们的学习(以下内容借鉴于leetcode)

首先介绍一下BFS,有文章是这么介绍BFS,我们有“一往无前的DFS”也有“齐头并进的BFS”,没错,BFS用通俗易懂的话来概括就是“齐头并进”

广度优先遍历」的思想在生活中随处可见:

如果我们要找一个医生或者律师,我们会先在自己的一度人脉中遍历(查找),如果没有找到,继续在自己的二度人脉中遍历(查找),直到找到为止。

把一块石头投入平静的水面,激起的一层一层波纹就呈现「广度优先遍历」的特点。

广度优先遍历呈现出「一层一层向外扩张」的特点,先看到的结点先遍历,后看到的结点后遍历,因此「广度优先遍历」可以借助「队列」实现。

说明:遍历到一个结点时,如果这个结点有左(右)孩子结点,依次将它们加入队列。

友情提示:广度优先遍历的写法相对固定,我们不建议大家背代码、记模板。在深刻理解广度优先遍历的应用场景(找无权图的最短路径),借助「队列」实现的基础上,多做练习,写对代码就是自然而然的事情了。

例题

  1. 岛屿数量

该题也是非常经典了,之前我们也已经使用DFS做过这道题了,今天我们再次将其拿出,用BFS做一遍~~~

为了求出岛屿的数量,我们可以扫描整个二维网格。如果一个位置为 1,则将其加入队列(注意哦,是将其对应的下标存放到队列中的)开始进行广度优先搜索。在广度优先搜索的过程中,每个搜索到的 1 都会被重新标记为 0     直到队列为空,搜索结束。 

class Solution {
public:
    int numIslands(vector<vector<char>>& grid) {
        int nr = grid.size();//行数
        if (!nr) return 0;//判断边界情况
        int nc = grid[0].size();//列数
 
        int num_islands = 0;//用于计数
        //遍历二维网格
        for (int r = 0; r < nr; ++r) {
            for (int c = 0; c < nc; ++c) {
                //满足条件时进来,否则进入下一次循环
                if (grid[r][c] == '1') {
                    ++num_islands;
                    grid[r][c] = '0';
                    //定义一个队列,用于存放下标信息
                    //注意对pair的理解,可以看作是内部有两个元素的结构体
                    queue<pair<int, int>> neighbors;
                    neighbors.push({r, c});//将'1'的下标信息入队
                    while (!neighbors.empty()) {
                        pair<int,int> rc = neighbors.front();//访问队首元素
                        neighbors.pop();//队首元素出队
                        int row = rc.first;//队首元素所对应的行号
                        int col = rc.second;//队首元素所对应的列号
                        //将它上下左右的‘1’都同化成‘0’
                        //上
                        //row - 1 >= 0 判断位置是否合法
                        if (row - 1 >= 0 && grid[row-1][col] == '1') {
                            neighbors.push({row-1, col});
                            grid[row-1][col] = '0';
                        }
                        //下
                        //row + 1 < nr 判断位置是否合法
                        if (row + 1 < nr && grid[row+1][col] == '1') {
                            neighbors.push({row+1, col});
                            grid[row+1][col] = '0';
                        }
                        //左
                        //col - 1 >= 0 判断位置是否合法
                        if (col - 1 >= 0 && grid[row][col-1] == '1') {
                            neighbors.push({row, col-1});
                            grid[row][col-1] = '0';
                        }
                        //右
                        //col + 1 < nc 判断位置是否合法
                        if (col + 1 < nc && grid[row][col+1] == '1') {
                            neighbors.push({row, col+1});
                            grid[row][col+1] = '0';
                        }
                    }
                }
            }
        }
 
        return num_islands;
    }
};

该代码较长,建议读者一边调试一边理解

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ZZZWWWFFF_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值