广度优先搜索(bfs)

学了将近半年的信息了,昨天猛地间发现我好像不会搜索。。。。

这就意味着我在noip的时候连暴力都不会打。。。为了避免这种事情的发生,我决定一定要好好学搜索。。

好了,废话不多说了,下面开始我们的正式话题:广度优先搜索

1.前言

  广度优先搜索其实是一种用来遍历连通图的一种算法,它的思想是从一个顶点V0开始,辐射状地优先遍历其周围较广的区域,故得名。                       貌似有的东西就真的跟徐大佬说的一样:说不清楚,只能靠自己去做题才能真正理解。

  所以,如果我说的你不是很明白,甚至都看不懂我在说什么,没事,自己去多刷两个题就好了  (⊙v⊙)

2.简单应用

  对于搜索这个东西是用来干嘛用的??

  大多数情况下我们在做迷宫类的问题的时候,大多要用搜索。我们从起点开始走到我们要到的终点,要求出我们走到终点的最短路。有的人又要说了:这不就是最短路问题吗?是,这的确就是最短路问题,但是最短路也是基于搜索而来的啊。况且我们从起点开始,找出到终点的最短路程,很多最短路径算法就是基于广度优先的思想成立的。

3.图的概念

前面说起了连通图,我觉得有必要再说一下图的概念(如果你已经知道了就可以直接看下一个模块)

(其实对于图我在自前就已经讲过,如果想看可以看看)~~~~(>_<)~~~~

如图所示,这就是我们所说的连通图,这里展示的是一个无向图,连通即每2个点都有至少一条路径相连,例如V0到V4的路径就是V0->V1->V4。

一般我们把顶点用V缩写,把边用E缩写。

4.广度优先搜索

§ 广度优先搜索基本思路

(1)先将图的根节点加入队列中

(2)从队中取出第一个元素判断它是否为所要找的节点,并将该结点从队中删除。若是,结束循环搜索,并返回结果。若不是,则将该点相连的未被访问过的直接子节点加入队中。

(3)若队列为空则说明改图已经全部访问过,及图中没有满足要求的结果,结束搜寻并回传“找不到目标”。

下面展示步骤2的解析图

  §

常常我们有这样一个问题,从一个起点开始要到一个终点,我们要找寻一条最短的路径,从图2-1举例,如果我们要求V0到V6的一条最短路(假设走一个节点按一步来算)【注意:此处你可以选择不看这段文字直接看图3-1】,我们明显看出这条路径就是V0->V2->V6,而不是V0->V3->V5->V6。先想想你自己刚刚是怎么找到这条路径的:首先看跟V0直接连接的节点V1、V2、V3,发现没有V6,进而再看刚刚V1、V2、V3的直接连接节点分别是:{V0、V4}、{V0、V1、V6}、{V0、V1、V5}(这里画删除线的意思是那些顶点在我们刚刚的搜索过程中已经找过了,我们不需要重新回头再看他们了)。这时候我们从V2的连通节点集中找到了V6,那说明我们找到了这条V0到V6的最短路径:V0->V2->V6,虽然你再进一步搜索V5的连接节点集合后会找到另一条路径V0->V3->V5->V6,但显然他不是最短路径。

你会看到这里有点像辐射形状的搜索方式,从一个节点,向其旁边节点传递病毒,就这样一层一层的传递辐射下去,知道目标节点被辐射中了,此时就已经找到了从起点到终点的路径。

我们采用示例图来说明这个过程,在搜索的过程中,初始所有节点是白色(代表了所有点都还没开始搜索),把起点V0标志成灰色(表示即将辐射V0),下一步搜索的时候,我们把所有的灰色节点访问一次,然后将其变成黑色(表示已经被辐射过了),进而再将他们所能到达的节点标志成灰色(因为那些节点是下一步搜索的目标点了),但是这里有个判断,就像刚刚的例子,当访问到V1节点的时候,它的下一个节点应该是V0和V4,但是V0已经在前面被染成黑色了,所以不会将它染灰色。这样持续下去,直到目标节点V6被染灰色,说明了下一步就到终点了,没必要再搜索(染色)其他节点了,此时可以结束搜索了,整个搜索就结束了。然后根据搜索过程,反过来把最短路径找出来,图3-1中把最终路径上的节点标志成绿色。

整个过程的实例图如图3-1所示。

初始全部都是白色(未访问)

即将搜索起点V0(灰色)

已搜索V0,即将搜索V1、V2、V3

……终点V6被染灰色,终止

找到最短路径

图3-1 寻找V0到V6的过程

  §广度优先搜索流程图

图3-2 广度优先搜索的流程图

  §实例

第一节就讲过广度优先搜索适用于迷宫类问题,这里先给出POJ3984《迷宫问题》。

《迷宫问题》

定义一个二维数组: 
int maze[5][5] = {
    0, 1, 0, 0, 0,
    0, 1, 0, 1, 0,
    0, 0, 0, 0, 0,
    0, 1, 1, 1, 0,
    0, 0, 0, 1, 0,
};
它表示一个迷宫,其中的1表示墙壁,0表示可以走的路,只能横着走或竖着走,不能斜着走,要求编程序找出从左上角到右下角的最短路线。

题目保证了输入是一定有解的。

也许你会问,这个跟广度优先搜索的图怎么对应起来?BFS的第一步就是要识别图的节点跟边!

  §识别出节点跟边

节点就是某种状态,边就是节点与节点间的某种规则。

对应于《迷宫问题》,你可以这么认为,节点就是迷宫路上的每一个格子(非墙),走迷宫的时候,格子间的关系是什么呢?按照题目意思,我们只能横竖走,因此我们可以这样看,格子与它横竖方向上的格子是有连通关系的,只要这个格子跟另一个格子是连通的,那么两个格子节点间就有一条边。

如果说本题再修改成斜方向也可以走的话,那么就是格子跟周围8个格子都可以连通,于是一个节点就会有8条边(除了边界的节点)。

  §解题思路

对应于题目的输入数组:

0, 1, 0, 0, 0,
    0, 1, 0, 1, 0,
    0, 0, 0, 0, 0,
    0, 1, 1, 1, 0,
    0, 0, 0, 1, 0,

我们把节点定义为(x,y),(x,y)表示数组maze的项maze[x][y]。

于是起点就是(0,0),终点是(4,4)。按照刚刚的思路,我们大概手工梳理一遍:

初始条件:

起点Vs为(0,0)

终点Vd为(4,4)

灰色节点集合Q={}

初始化所有节点为白色节点

开始我们的广度搜索!

手工执行步骤【PS:你可以直接看图】:

1.起始节点Vs变成灰色,加入队列Q,Q={(0,0)}

2.取出队列Q的头一个节点Vn,Vn={0,0},Q={}

3.把Vn={0,0}染成黑色,取出Vn所有相邻的白色节点{(1,0)}

4.不包含终点(4,4),染成灰色,加入队列Q,Q={(1,0)}

5.取出队列Q的头一个节点Vn,Vn={1,0},Q={}

6.把Vn={1,0}染成黑色,取出Vn所有相邻的白色节点{(2,0)}

7.不包含终点(4,4),染成灰色,加入队列Q,Q={(2,0)}

8.取出队列Q的头一个节点Vn,Vn={2,0},Q={}

9.把Vn={2,0}染成黑色,取出Vn所有相邻的白色节点{(2,1), (3,0)}

10.不包含终点(4,4),染成灰色,加入队列Q,Q={(2,1), (3,0)}

11.取出队列Q的头一个节点Vn,Vn={2,1},Q={(3,0)}

12. 把Vn={2,1}染成黑色,取出Vn所有相邻的白色节点{(2,2)}

13.不包含终点(4,4),染成灰色,加入队列Q,Q={(3,0), (2,2)}

14.持续下去,知道Vn的所有相邻的白色节点中包含了(4,4)……

15.此时获得了答案

起始你很容易模仿上边过程走到终点,那为什么它就是最短的呢?

怎么保证呢?

我们来看看广度搜索的过程中节点的顺序情况:

图4-1 迷宫问题的搜索树

你是否观察到了,广度搜索的顺序是什么样子的?

图中标号即为我们搜索过程中的顺序,我们观察到,这个搜索顺序是按照上图的层次关系来的,例如节点(0,0)在第1层,节点(1,0)在第2层,节点(2,0)在第3层,节点(2,1)和节点(3,0)在第3层。

我们的搜索顺序就是第一层->第二层->第三层->第N层这样子。

我们假设终点在第N层,因此我们搜索到的路径长度肯定是N,而且这个N一定是所求最短的。

我们用简单的反证法来证明:假设终点在第N层上边出现过,例如第M层,M<N,那么我们在搜索的过程中,肯定是先搜索到第M层的,此时搜索到第M层的时候发现终点出现过了,那么最短路径应该是M,而不是N了。

所以根据广度优先搜索的话,搜索到终点时,该路径一定是最短的。

对于上面那个题我来写写核心代码

§核心代码

/**
 * 广度优先搜索
 * @param Vs 起点
 * @param Vd 终点
 */
bool BFS(Node& Vs, Node& Vd){
    queue<Node> Q;
    Node Vn, Vw;
    int i;  

    //用于标记颜色当visit[i][j]==true时,说明节点访问过,也就是黑色
    bool visit[MAXL][MAXL];  

    //四个方向
    int dir[][2] = {
        {0, 1}, {1, 0},
        {0, -1}, {-1, 0}
    };  

    //初始状态将起点放进队列Q
    Q.push(Vs);
    visit[Vs.x][Vs.y] = true;//设置节点已经访问过了!  

    while (!Q.empty()){//队列不为空,继续搜索!
        //取出队列的头Vn
        Vn = Q.front();
        Q.pop();  

        for(i = 0; i < 4; ++i){
            Vw = Node(Vn.x+dir[i][0], Vn.y+dir[i][1]);//计算相邻节点  

            if (Vw == Vd){//找到终点了!
                //把路径记录,这里没给出解法
                return true;//返回
            }  

            if (isValid(Vw) && !visit[Vw.x][Vw.y]){
                //Vw是一个合法的节点并且为白色节点
                Q.push(Vw);//加入队列Q
                visit[Vw.x][Vw.y] = true;//设置节点颜色
            }
        }
    }
    return false;//无解
}  

好了就先说这些吧。。。

时间: 2024-12-09 03:14:46

广度优先搜索(bfs)的相关文章

广度优先搜索(BFS)

广度优先 Description: 阿狸被困在迷宫,snoopy要去救他,snoopy可以向上.下.左.右四个方向行走,每走一步(格)就要喝掉一瓶益力多.现在给它一个迷宫地图请问:snoopy最少需要多少瓶益力多才能走出迷宫? Input: 先输入一个数t,表示测试的数据个数, 下面输入的就是t个迷宫, 每个迷宫的输入都应包含以下数据, 输入迷宫的大小 n(n<=15),表示迷宫大小为n*n. 再输入迷宫, 用大写字母“S”表示snoopy的位置, 用小写字母“E”表示阿狸被困的位置, 用“.”

【算法导论】--C++实现广度优先搜索bfs

一.题目 根据上次随机生成的100个顶点的无向图和有向图,对其进行广度优先搜索. 二.理解广度优先搜索 广度优先搜索可以将其想象成水滴落入水面溅起了的一圈一圈的涟漪,是由一个起始点开始一圈一圈进行扩散搜索的. [课上老师是这样说的,大家想象一下,发现其实非常形象] 广度优先搜索总是从一个起始点出发,首先扩散这个点周围所有的邻居,然后邻居在去扩散邻居的邻居(*^-^*)...然后一直到最后将整张图都扩散完. 三.代码实现 对于第一次随机生成100个顶点的图进行了细节的修改,将每个顶点的类型改为了自

深度优先搜索DFS和广度优先搜索BFS

DFS简介 深度优先搜索,从起点开始按照某个原则一直往深处走,直到找到解,或者走不下去,走不下去则回溯到前一节点选择另一条路径走,直到找到解为止. BFS简介 广度优先搜索,从起点开始先搜索其相邻的节点,由此向外不断扩散,直到找到解为止. 举例解释 从1开始去寻找5 DFS: 原则:优先选择左手边 过程:1-2-3-4-6-4-5 BFS: 队列情况:1 2.5     5.3 5出来则找到 遍历图中所有点 DFS: 原则:优先选择左手边 过程:1-2-3-4-6-4-5 BFS: 队列情况:1

算法与数据结构基础 - 广度优先搜索(BFS)

BFS基础 广度优先搜索(Breadth First Search)用于按离始节点距离.由近到远渐次访问图的节点,可视化BFS 通常使用队列(queue)结构模拟BFS过程,关于queue见:算法与数据结构基础 - 队列(Queue) 最直观的BFS应用是图和树的遍历,其中图常用邻接表或矩阵表示,例如 LeetCode题目 690. Employee Importance: // LeetCode 690. Employee Importance/* class Employee { publi

广度优先搜索BFS

广度优先搜索可以形成一个广度优先搜索树 算法时间为O(V+E),两重循环 输入:图g,起点start(int) 需要的数据结构:队列Q.color数组(存放每个顶点的颜色) 算法过程: 1. 预处理:1)color数组的每个值都赋为white(表示没被访问过):2)队列Q为空队列 2. 处理起点: 1)color[start]=gray,gray表示顶点已被访问,但其子节点未被处理(指的是入队列):2)Q.enQueue(start) 3. 循环以下操作,直到Q为空 1)int u=Q.deQu

广度优先搜索bfs C++实现

#include<iostream>  #include<vector>  #include<map>  #include<queue>  #include<set>  using namespace std;    vector<int> bfs(map<int, vector<int> > link, int top)  {      queue<int> qe;  //队列用来记录节点的遍历顺

PAT甲级——1091 Acute Stroke (广度优先搜索BFS)

本文同步发布在CSDN:https://blog.csdn.net/weixin_44385565/article/details/94207638 1091 Acute Stroke (30 分) One important factor to identify acute stroke (急性脑卒中) is the volume of the stroke core. Given the results of image analysis in which the core regions

数据结构之 图论---基于邻接矩阵的广度优先搜索遍历(输出bfs遍历序列)

数据结构实验图论一:基于邻接矩阵的广度优先搜索遍历 Time Limit: 1000MS Memory limit: 65536K 题目描述 给定一个无向连通图,顶点编号从0到n-1,用广度优先搜索(BFS)遍历,输出从某个顶点出发的遍历序列.(同一个结点的同层邻接点,节点编号小的优先遍历) 输入 输入第一行为整数n(0< n <100),表示数据的组数. 对于每组数据,第一行是三个整数k,m,t(0<k<100,0<m<(k-1)*k/2,0< t<k),

GraphMatrix::BFS广度优先搜索

查找某一结点的邻居: virtual int firstNbr(int i) { return nextNbr(i, n); } //首个邻接顶点 virtual int nextNbr(int i, int j) //相对于顶点j的下一邻接顶点 { while ((-1 < j) && (!exists(i, --j))); return j; } //逆向线性试探(改用邻接表可提高效率) 对于图中的所有顶点,对每一个连通区域进行BFS: template <typename

关于宽搜BFS广度优先搜索的那点事

以前一直知道深搜是一个递归栈,广搜是队列,FIFO先进先出LILO后进后出啥的.DFS是以深度作为第一关键词,即当碰到岔道口时总是先选择其中的一条岔路前进,而不管其他岔路,直到碰到死胡同时才返回岔道口并选择其他岔路.接下来将介绍的广度优先搜索(Breadth First Search, BFS)则是以广度为第一关键词,当碰到岔道口时,总是先一次访问从该岔道口能直接到达的所有节结点,然后再按这些结点被访问的顺序去依次访问它们能直接到达的所有结点,以此类推,直到所有结点都被访问为止.这就跟平静的水面