图的广度优先搜索(BFS)和深度优先搜索(DFS)算法解析
BFS和DFS算法解析
【算法入门】
2018/6/2
1.前言
和树的遍历类似,图的遍历也是从图中某点出发,然后按照某种方法对图中所有顶点进行访问,且仅访问一次。
但是图的遍历相对树而言要更为复杂。因为图中的任意顶点都可能与其他顶点相邻,所以在图的遍历中必须记录已被访问的顶点,避免重复访问。
根据搜索路径的不同,我们可以将遍历图的方法分为两种:广度优先搜索和深度优先搜索。
2.图的基本概念
2.1.无向图和无向图
顶点对(u,v)是无序的,即(u,v)和(v,u)是同一条边。常用一对圆括号表示。
图2-1-1 无向图示例
顶点对<u,v>是有序的,它是指从顶点u到顶点 v的一条有向边。其中u是有向边的始点,v是有向边的终点。常用一对尖括号表示。
图2-1-2 有向图示例
2.2.权和网
图的每条边上可能存在具有某种含义的数值,称该数值为该边上的权。而这种带权的图被称为网。
2.3.连通图与非连通图
连通图:在无向图G中,从顶点v到顶点v'有路径,则称v和v'是联通的。若图中任意两顶点v、v'∈V,v和v'之间均联通,则称G是连通图。上述两图均为连通图。
非连通图:若无向图G中,存在v和v'之间不连通,则称G是非连通图。
图2-3 非连通图示例
3.广度优先搜索
3.1.算法的基本思路
广度优先搜索类似于树的层次遍历过程。它需要借助一个队列来实现。如图2-1-1所示,要想遍历从v0到v6的每一个顶点,我们可以设v0为第一层,v1、v2、v3为第二层,v4、v5为第三层,v6为第四层,再逐个遍历每一层的每个顶点。
具体过程如下:
1.准备工作:创建一个visited数组,用来记录已被访问过的顶点;创建一个队列,用来存放每一层的顶点;初始化图G。
2.从图中的v0开始访问,将的visited[v0]数组的值设置为true,同时将v0入队。
3.只要队列不空,则重复如下操作:
(1)队头顶点u出队。
(2)依次检查u的所有邻接顶点w,若visited[w]的值为false,则访问w,并将visited[w]置为true,同时将w入队。
3.2.算法的实现过程
白色表示未被访问,灰色表示即将访问,黑色表示已访问。
visited数组:0表示未访问,1表示以访问。
队列:队头出元素,队尾进元素。
1.初始时全部顶点均未被访问,visited数组初始化为0,队列中没有元素。
图3-2-1
2.即将访问顶点v0。
图3-2-2
3.访问顶点v0,并置visited[0]的值为1,同时将v0入队。
图3-2-3
4.将v0出队,访问v0的邻接点v2。判断visited[2],因为visited[2]的值为0,访问v2。
图3-2-4
5.将visited[2]置为1,并将v2入队。
图3-2-5
6.访问v0邻接点v1。判断visited[1],因为visited[1]的值为0,访问v1。
图3-2-6
7.将visited[1]置为0,并将v1入队。
图3-2-7
8.判断visited[3],因为它的值为0,访问v3。将visited[3]置为0,并将v3入队。
图3-2-8
9.v0的全部邻接点均已被访问完毕。将队头元素v2出队,开始访问v2的所有邻接点。
开始访问v2邻接点v0,判断visited[0],因为其值为1,不进行访问。
继续访问v2邻接点v4,判断visited[4],因为其值为0,访问v4,如下图:
图3-2-9
10.将visited[4]置为1,并将v4入队。
图3-2-10
11.v2的全部邻接点均已被访问完毕。将队头元素v1出队,开始访问v1的所有邻接点。
开始访问v1邻接点v0,因为visited[0]值为1,不进行访问。
继续访问v1邻接点v4,因为visited[4]的值为1,不进行访问。
继续访问v1邻接点v5,因为visited[5]值为0,访问v5,如下图:
图3-2-11
12.将visited[5]置为1,并将v5入队。
图3-2-12
13.v1的全部邻接点均已被访问完毕,将队头元素v3出队,开始访问v3的所有邻接点。
开始访问v3邻接点v0,因为visited[0]值为1,不进行访问。
继续访问v3邻接点v5,因为visited[5]值为1,不进行访问。
图3-2-13
14.v3的全部邻接点均已被访问完毕,将队头元素v4出队,开始访问v4的所有邻接点。
开始访问v4的邻接点v2,因为visited[2]的值为1,不进行访问。
继续访问v4的邻接点v6,因为visited[6]的值为0,访问v6,如下图:
图3-2-14
15.将visited[6]值为1,并将v6入队。
图3-2-15
16.v4的全部邻接点均已被访问完毕,将队头元素v5出队,开始访问v5的所有邻接点。
开始访问v5邻接点v3,因为visited[3]的值为1,不进行访问。
继续访问v5邻接点v6,因为visited[6]的值为1,不进行访问。
图3-2-16
17.v5的全部邻接点均已被访问完毕,将队头元素v6出队,开始访问v6的所有邻接点。
开始访问v6邻接点v4,因为visited[4]的值为1,不进行访问。
继续访问v6邻接点v5,因为visited[5]的值文1,不进行访问。
图3-2-17
18.队列为空,退出循环,全部顶点均访问完毕。
3.3具体代码的实现
3.3.1用邻接矩阵表示图的广度优先搜索
/*一些量的定义*/
queue<char> q; //定义一个队列,使用库函数queue
#define MVNum 100 //表示最大顶点个数
bool visited[MVNum]; //定义一个visited数组,记录已被访问的顶点
/*邻接矩阵存储表示*/
typedef struct AMGraph
{char vexs[MVNum]; //顶点表int arcs[MVNum][MVNum]; //邻接矩阵int vexnum, arcnum; //当前的顶点数和边数
}AMGraph;
/*找到顶点v的对应下标*/
int LocateVex(AMGraph &G, char v)
{int i;for (i = 0; i < G.vexnum; i++)if (G.vexs[i] == v)return i;
}
/*采用邻接矩阵表示法,创建无向图G*/
int CreateUDG_1(AMGraph &G)
{int i, j, k;char v1, v2;scanf("%d%d", &G.vexnum, &G.arcnum); //输入总顶点数,总边数getchar(); //获取'\n’,防止其对之后的字符输入造成影响for (i = 0; i < G.vexnum; i++) scanf("%c", &G.vexs[i]); //依次输入点的信息for (i = 0; i < G.vexnum; i++)for (j = 0; j < G.vexnum; j++)G.arcs[i][j] = 0; //初始化邻接矩阵边,0表示顶点i和j之间无边for (k = 0; k < G.arcnum; k++){getchar();scanf("%c%c", &v1, &v2); //输入一条边依附的顶点i = LocateVex(G, v1); //找到顶点i的下标j = LocateVex(G, v2); //找到顶点j的下标G.arcs[i][j] = G.arcs[j][i] = 1; //1表示顶点i和j之间有边,无向图不区分方向}return 1;
}
/*采用邻接矩阵表示图的广度优先遍历*/
void BFS_AM(AMGraph &G,char v0)
{
/*从v0元素开始访问图*/int u,i,v,w;v = LocateVex(G,v0); //找到v0对应的下标printf("%c ", v0); //打印v0visited[v] = 1; //顶点v0已被访问q.push(v0); //将v0入队while (!q.empty()){u = q.front(); //将队头元素u出队,开始访问u的所有邻接点v = LocateVex(G, u); //得到顶点u的对应下标q.pop(); //将顶点u出队for (i = 0; i < G.vexnum; i++){w = G.vexs[i];if (G.arcs[v][i] && !visited[i])//顶点u和w间有边,且顶点w未被访问{printf("%c ", w); //打印顶点wq.push(w); //将顶点w入队visited[i] = 1; //顶点w已被访问}}}
}
3.3.2用邻接表表示图的广度优先搜索
/*找到顶点对应的下标*/
int LocateVex(ALGraph &G, char v)
{int i;for (i = 0; i < G.vexnum; i++)if (v == G.vertices[i].data)return i;
}
/*邻接表存储表示*/
typedef struct ArcNode //边结点
{int adjvex; //该边所指向的顶点的位置ArcNode *nextarc; //指向下一条边的指针int info; //和边相关的信息,如权值
}ArcNode;typedef struct VexNode //表头结点
{char data; ArcNode *firstarc; //指向第一条依附该顶点的边的指针
}VexNode,AdjList[MVNum]; //AbjList表示一个表头结点表typedef struct ALGraph
{AdjList vertices;int vexnum, arcnum;
}ALGraph;
/*采用邻接表表示法,创建无向图G*/
int CreateUDG_2(ALGraph &G)
{int i, j, k;char v1, v2;scanf("%d%d", &G.vexnum, &G.arcnum); //输入总顶点数,总边数getchar();for (i = 0; i < G.vexnum; i++) //输入各顶点,构造表头结点表{scanf("%c", &G.vertices[i].data); //输入顶点值G.vertices[i].firstarc = NULL; //初始化每个表头结点的指针域为NULL}for (k = 0; k < G.arcnum; k++) //输入各边,构造邻接表{getchar();scanf("%c%c", &v1, &v2); //输入一条边依附的两个顶点i = LocateVex(G, v1); //找到顶点i的下标j = LocateVex(G, v2); //找到顶点j的下标ArcNode *p1 = new ArcNode; //创建一个边结点*p1p1->adjvex = j; //其邻接点域为jp1->nextarc = G.vertices[i].firstarc; G.vertices[i].firstarc = p1; // 将新结点*p插入到顶点v1的边表头部ArcNode *p2 = new ArcNode; //生成另一个对称的新的表结点*p2p2->adjvex = i;p2->nextarc = G.vertices[j].firstarc;G.vertices[j].firstarc = p1;}return 1;
}
/*采用邻接表表示图的广度优先遍历*/
void BFS_AL(ALGraph &G, char v0)
{int u,w,v;ArcNode *p;printf("%c ", v0); //打印顶点v0v = LocateVex(G, v0); //找到v0对应的下标visited[v] = 1; //顶点v0已被访问q.push(v0); //将顶点v0入队while (!q.empty()){u = q.front(); //将顶点元素u出队,开始访问u的所有邻接点v = LocateVex(G, u); //得到顶点u的对应下标q.pop(); //将顶点u出队for (p = G.vertices[v].firstarc; p; p = p->nextarc) //遍历顶点u的邻接点{w = p->adjvex; if (!visited[w]) //顶点p未被访问{printf("%c ", G.vertices[w].data); //打印顶点pvisited[w] = 1; //顶点p已被访问q.push(G.vertices[w].data); //将顶点p入队}}}
}
3.4.非联通图的广度优先遍历的实现方法
/*广度优先搜索非连通图*/
void BFSTraverse(AMGraph G)
{int v;for (v = 0; v < G.vexnum; v++)visited[v] = 0; //将visited数组初始化for (v = 0; v < G.vexnum; v++)if (!visited[v]) BFS_AM(G, G.vexs[v]); //对尚未访问的顶点调用BFS
}
4.深度优先搜索
4.1算法的基本思路
深度优先搜索类似于树的先序遍历,具体过程如下:
准备工作:创建一个visited数组,用于记录所有被访问过的顶点。
1.从图中v0出发,访问v0。
2.找出v0的第一个未被访问的邻接点,访问该顶点。以该顶点为新顶点,重复此步骤,直至刚访问过的顶点没有未被访问的邻接点为止。
3.返回前一个访问过的仍有未被访问邻接点的顶点,继续访问该顶点的下一个未被访问领接点。
4.重复2,3步骤,直至所有顶点均被访问,搜索结束。
4.2算法的实现过程
1.初始时所有顶点均未被访问,visited数组为空。
图4-2-1
2.即将访问v0。
图4-2-2
3.访问v0,并将visited[0]的值置为1。
图4-2-3
4.访问v0的邻接点v2,判断visited[2],因其值为0,访问v2。
图4-2-4
5.将visited[2]置为1。
图4-2-5
6.访问v2的邻接点v0,判断visited[0],其值为1,不访问。
继续访问v2的邻接点v4,判断visited[4],其值为0,访问v4。
图4-2-6
7.将visited[4]置为1。
图4-2-7
8.访问v4的邻接点v1,判断visited[1],其值为0,访问v1。
图4-2-8
9.将visited[1]置为1。
图4-2-9
10.访问v1的邻接点v0,判断visited[0],其值为1,不访问。
继续访问v1的邻接点v4,判断visited[4],其值为1,不访问。
继续访问v1的邻接点v5,判读visited[5],其值为0,访问v5。
图4-2-10
11.将visited[5]置为1。
图4-2-11
12.访问v5的邻接点v1,判断visited[1],其值为1,不访问。
继续访问v5的邻接点v3,判断visited[3],其值为0,访问v3。
图4-2-12
13.将visited[1]置为1。
图4-2-13
14.访问v3的邻接点v0,判断visited[0],其值为1,不访问。
继续访问v3的邻接点v5,判断visited[5],其值为1,不访问。
v3所有邻接点均已被访问,回溯到其上一个顶点v5,遍历v5所有邻接点。
访问v5的邻接点v6,判断visited[6],其值为0,访问v6。
图4-2-14
15.将visited[6]置为1。
图4-2-15
16.访问v6的邻接点v4,判断visited[4],其值为1,不访问。
访问v6的邻接点v5,判断visited[5],其值为1,不访问。
v6所有邻接点均已被访问,回溯到其上一个顶点v5,遍历v5剩余邻接点。
图4-2-16
17.v5所有邻接点均已被访问,回溯到其上一个顶点v1。
v1所有邻接点均已被访问,回溯到其上一个顶点v4,遍历v4剩余邻接点v6。
v4所有邻接点均已被访问,回溯到其上一个顶点v2。
v2所有邻接点均已被访问,回溯到其上一个顶点v1,遍历v1剩余邻接点v3。
v1所有邻接点均已被访问,搜索结束。
图4-2-17
4.3具体代码实现
4.3.1用邻接矩阵表示图的深度优先搜索
邻接矩阵的创建在上述已描述过,这里不再赘述
void DFS_AM(AMGraph &G, int v)
{int w;printf("%c ", G.vexs[v]);visited[v] = 1;for (w = 0; w < G.vexnum; w++)if (G.arcs[v][w]&&!visited[w]) //递归调用DFS_AM(G,w);
}
4.3.2用邻接表表示图的深度优先搜素
邻接表的创建在上述已描述过,这里不再赘述。
void DFS_AL(ALGraph &G, int v)
{int w;printf("%c ", G.vertices[v].data);visited[v] = 1;ArcNode *p = new ArcNode;p = G.vertices[v].firstarc;while (p){w = p->adjvex;if (!visited[w]) DFS_AL(G, w);p = p->nextarc;}
}
图的广度优先搜索(BFS)和深度优先搜索(DFS)算法解析相关推荐
- matlab bfs函数,matlab练习程序(广度优先搜索BFS、深度优先搜索DFS)
如此经典的算法竟一直没有单独的实现过,真是遗憾啊. 广度优先搜索在过去实现的二值图像连通区域标记和prim最小生成树算法时已经无意识的用到了,深度优先搜索倒是没用过. 这次单独的将两个算法实现出来,因 ...
- 图论算法(5):图的广度优先遍历 BFS
本章节内容使用 java 实现,Github 代码仓:https://github.com/ZhekaiLi/Code/tree/main/Graph/src 查看文章内的图片可能需要科学上网! 因为 ...
- 【vivo2021届秋季校招编程题】【java】广度优先搜索(BFS)/深度优先搜索(DFS)找最短路径长度
vivo2021届秋季校招编程题 图中 找两点间的最短路径长度 广度搜索bfs/深度搜索dfs vivo游戏中心的运营小伙伴最近接到一款新游戏的上架申请,为了保障用户体验,运营同学将按运营流程和规范对 ...
- 广度优先搜索(BSF)和深度优先搜索(DSF)示例
输入数据 示例代码 #include <iostream> #include <queue> #include <stack> #include <vecto ...
- java数据结构和算法——图的广度优先(BFS)遍历
目录 一.图的遍历介绍 二.图的广度优先搜索(Broad First Search) 三.图的广度优先遍历算法步骤 四.图的广度优先遍历示例需求 五.图的广度优先遍历代码示例 一.图的遍历介绍 所谓图 ...
- 算法训练营 搜索技术(深度优先搜索)
回溯法 回溯法指从初始状态出发,按照深度优先搜索的方式,根据产生子节点的条件约束,搜索问题的解,当发现当前节点不满足求解条件时,就回溯,尝试其他路径. 回溯法是一种"能进则进,进不了就换,换 ...
- 搜索专题之深度优先搜索(DFS)
什么是DFS? 深度优先遍历图的方法是,从图中某顶点v出发: (1)访问顶点v: (2)依次从v的未被访问的邻接点出发,对图进行深度优先遍历:直至图中和v有路径相通的顶点都被访问: (3)若此时图中尚 ...
- 图的广度优先搜索(bfs)以及深度优先搜索(dfs)
1.前言 和树的遍历类似,图的遍历也是从图中某点出发,然后按照某种方法对图中所有顶点进行访问,且仅访问一次. 但是图的遍历相对树而言要更为复杂.因为图中的任意顶点都可能与其他顶点相邻,所以在图的遍历中 ...
- 深度优先搜索(DFS)与广度优先搜索(BFS)算法详解
深度优先搜索(DFS)与广度优先搜索(BFS)详解 1.广度优先搜索算法 1.1.前言 和树的遍历类似,图的遍历也是从图中某点出发,然后按照某种方法对图中所有顶点进行访问,且仅访问一次. 但是图的遍历 ...
最新文章
- webpack热更新实现
- Golang init函数执行顺序
- js优化阿里云图片加载(一)
- R语言之离群点检验(part3)--利用聚类检测离群点
- 灾备还缺一套评价体系
- 第 2-2 课:各种内部类和枚举类 + 面试题
- 这个工具可以组合参数画出2种单细胞Marker显示图
- HttpClient4.3.x的连接管理
- 黄奕:我之前弄了一个母婴网站,根本就不会做生意,几个月上千万就赔进去了
- python查天气预报_一个用Python编写抓取天气预报的代码示例
- mysql my.ini设置root密码_mysql 5.7设置root密码 windows
- numpy保存和读取dictionary字典
- Shell脚本中使用awk进行空格分词
- ASP.NET Core MVC 打造一个简单的图书馆管理系统 (修正版)(二)数据库初始化、基本登录页面以及授权逻辑的建立...
- 简易c语言编程软件,c语言开发工具下载
- UE4 下载安装操作步骤
- JDBC个人学习总结
- 小程序 蓝牙连接(出现的问题和一些解决方法)
- 蘑菇街三人斗地主随机发牌的笔试题
- 一个Android开发者自学Python的心路历程
热门文章
- 基于实时计算(flink)打造舆情分析平台——新华智云
- win7计算机限制不能安装,win7怎么禁止安装软件_windows7禁止安装软件的方法
- excel2016 mysql_Excel2016新功能尝鲜-MySQL连接
- 人工智能-用matlab实现数字识别
- 在 sys.servers 中找不到服务器 ‘xxxxxx‘。请验证指定的服务器名称是否正确。
- 如何判断一个男人将来是穷还是富?
- Python爬虫(5):豆瓣读书练手爬虫
- 机刷——App推广作弊内幕系列
- 小米手环2来电不震动,来电不提醒怎么办
- Android 腾讯位置服务地图简单使用