第五章:图(图的遍历操作)

1.图的遍历

图的遍历:从图中某一顶点出发,按照某种搜索方法沿着图中的边对图中的所有顶点访问依次且仅访问一次

其实树的层次遍历和图的广度优先搜索类似,可以把这个二叉树看成一个图


2.广度优先搜索(BFS)

广度优先搜索

  • 首先访问起始顶点v
  • 接着由v出发依次访问v的各个 未被访问过 的邻接顶点w1,w1....wi
  • 然后依次访问w1,w2....wi的所有 未被访问过 的邻接顶点
  • 在从这些访问过的顶点出发,访问它们所有 未被访问过 的邻接顶点
  • 以此类推

如上图,它的 广度优先搜索遍历 ,我们如果按照 树的层次遍历 这种方式进行遍历它的过程如下:

首先 1,然后访问它的所有邻接顶点即 2,3,然后分别访问 2 和 3 的邻接顶点,2 的 4 和5 ,3 的 6 ,接着我们从顶点 4 出发依旧访问它的所有邻接顶点即 7 和 5,此时我们就会发生错误,因为顶点 5 我们已经访问过了,再访问就不符合 广度优先搜索 的要求了, 所以我们遍历的时候不能完全按照树的层次遍历的方式进行遍历图 ,那么怎么实现图的广度优先搜索呢?

我们知道树的层次遍历我们借助了一种特殊的数据结构:队列

那么图我们不仅仅要依靠队列还要依靠一个辅助标记数组即:队列+辅助标记数组

辅助标记数组

我们依旧使用上面的图,首先我们需要初始化,即将辅助标记数组中的值全部置为0,即0表示未被访问,1表示已被访问。重新进行遍历:从 1 出发,首先 1 入队,接着修改 v[0]=1 ,接着出队队首元素 1 并访问,接着我们需要将 1 的邻接顶点 2 和 3 一次入队,接着修改 v[1]=1,v[2]=1 ,然后出队队首元素 2 并进行访问,接着我们需要将顶点 2 的邻接顶点分别 1,4,5进行入队,这里我们其实只入队了 4 和 5,接着修改 v[3]=1,v[4]=1 ,这里有一个判断过程就是利用这个辅助标记数组,接着出队队首队首元素 3 并访问,然后将顶点 3 邻接的顶点并且未入队的顶点进行入队操作,即顶点 6 入队,同时修改a[5]=1,接着出队队首元素 4 并访问,然后入队 顶点 4 的邻接的顶点并且未入队的顶点 7 ,同时修改 v[6]=1,接着出队队首元素 5 并访问,接着出队队首元素 7 并访问,接着出队队首元素 7 并访问,遍历完成。此时数组中所有顶点的值都是1.

代码实现

bool visited[MAX_TREE_SIZE] void BFSTraverse(Graph G){      for(int i=0; i        visited[i]=FALSE;    }    InitQueue(Q);    //for循环的作用,我们上面讲的是一个连通图,所有顶点都可以通过一个顶点依次进行访问    //但是如果一个图不是连通的,我们需要遍历所有顶点    for(int i=0;i        if(!visited(i)){            BFS(G,i);        }    }}//广度优先搜索void BFS(Graph G,int V){    visit(v); //访问    visited=TRUE; //TRUE 入队了,FALSE未入队    EnQueue(Q,v); //将结点入队    while(!isEmpty(Q)){ //判断队列是否是空        DeQueue(Q,v); //出队队首元素,并赋值到v中        //FirstNeighbor 求图G中顶点x的第一个邻接顶点,存在返回顶点号,不存在返-1        //判断 w是否大于0,如果是-1则说明没有邻接顶点了        //NextNeighbor 求图G中顶点v的的下一个邻接顶点并赋值给w        for(int w=FirstNeighbor(G,v);w>0;w=NextNeighbor(G,v,w)){             if(!visited[w]){//判断是否入队过                visit[w];                visited[w]=TRUE;                EnQueue(Q,w);            }        }    }}

3.应用

3.1无权图单源最短路径问题

定义从顶点u到顶点v的最短路径d(u,v)为从u到v的任何路径中最少的边数,若从u到v没有通路,则d(u,v)=∞(表示不可达到)

//和广度优先搜索相似,增加了保存最短路径的一个数组void BFS_MIN_Distance(Graph G,int u){//传入图 和 初始顶点    for(int i=0;i        d[i]=MAX; //保存最短路径的值,我们初始化为最大值    }    visited[u]=TRUE;//标识为该顶点已经入队    d[u]=0;//初始顶点路径值改为0    EnQueue(Q,u);//入队    while(!isEmpty(Q)){//判断队列时候为空     DeQueue(Q,u);//出队队首元素        //FirstNeighbor 求图G中顶点x的第一个邻接顶点,存在返回顶点号,不存在返-1        //判断 w是否大于0,如果是-1则说明没有邻接顶点了        //NextNeighbor 求图G中顶点v的的下一个邻接顶点并赋值给w        for(int w=FirstNeighbor(G,u);w>0;w=NextNeighbor(G,u,w)){            if(!visit[w]){//判断该顶点是否已经被访问过                visited[w]=TRUE;//设置被访问过                //d[u]表示到初始顶点的最短路径,w是它的邻接点,所以+1等目前w到初始顶点的最短路径                d[w]=d[u]+1;                EnQueue(Q,w);//入队            }        }     }}

3.2广度优先生成树

广度优先生成树:在广度遍历过程中,我们可以得到一颗遍历树,称为广度优先生成树(生成森林)

如果是一个连通图我们会得到是一颗生成树,而如果是非连通图我们得到的是生成森林

连通图:任意两个结点都是连通的


邻接矩阵法的广度优先生成树是唯一的,邻接表法的不唯一

因为一个图的邻接矩阵表示是唯一的,所以我们在进行遍历的过程也是唯一的,但是邻接表表示法中我们输入的次序不唯一生成的边表就不唯一,对应遍历的过程(遍历边的次序)就不唯一了

4.深度优先搜索(DFS)

广度优先搜索和树层次遍历比较类似,而深度优先搜索和树先序遍历比较类似,如果把这样的一个树看成一个图,它的先序遍历顺序就是图的深度优先搜索遍历顺序


我们可以发现广度优先搜索是按照图的宽度范围进行遍历,而深度优先搜索是按照一条路径的深度的走向进行搜索遍历的

深度优先搜索(DFS)

  • 首先访问起始顶点v
  • 接着由v出发访问v的任意一个邻接且未被访问的邻接顶点Wi
  • 然后再访问与Wi邻接且未被访问的任意顶点Yi
  • 若Wi没有邻接且未被访问的顶点时,退回到它的上一层顶点v
  • 重复上述过程,直到所有顶点被访问为止

我们通过上面的算法思想遍历一遍上图:首先访问 1 ,接着可以任意访问顶点 2 或 3,我们访问 2,接着我们可以  访问任意顶点 4 或 5,我们访问 4 ,然后我们可以访问任意的顶点 7 或者 5 (7和5也是4的邻接顶点),假设我们访问5,然后 5 没有邻接且未被访问的顶点我们退回到 4 ,接着访问顶点 7,接着退回到顶点 1 ,然后访问顶点 3 ,接着访问 6,遍历完成:1 2 4 5 7 3 6

我们从上面的遍历过程可以发现整个过程可以使用递归来实现,当然递归也可以转换成来实现,同时我们也需要一个辅助标记数组。即:递归(栈)+辅助标记数组

代码实现

bool visited[MAX_TREE_SIZE]//辅助标记数组void DFSTraverse(Graph G){    for(int i=0;i        visited[i]=FALSE;//初始化所有结点都未必访问    }    for(int i=0;i        if(!visited[i]){//如果结点未被访问            DFS(G,i);//G:图,i:起始顶点的编号        }    }}void DFS(Graph G,int v){    visit(v); //访问    visited[v]=TRUE;//置为访问过    for(int w=FirstNeighbor(G,v);w>0;w=NextNeighbor(G,v,w)){            if(!visit[w]){//判断该顶点是否已经被访问过                DFS(G,w);//递归            }    }}

如上图,我们通过上面的代码来遍历一遍:从A出发,访问A并把A对应的辅助标记数组值设置为TREUE,接着我们找A的第一个邻接顶点比如是C,然后我们判断C没有被访问过,接着继续调用DFS函数,访问C,同理继续找C的第一个邻接顶点D,判断D没有被访问过,我们访问顶点D,然后D没有邻接顶点此函数结束,我们退回到访问C的顶点的DFS的for循环中,找到C的第二个邻接点E,判断E没有被方问过,我们访问E,接着E没有邻接顶点,继续退回到C,C没有未被访问过的邻接顶点,我们退回到A的DFS的for循环中,发现A的第二个邻接顶点E被访问过了,所以A也没有未被访问过的邻接顶点了,我们退回到了DFSTraverse函数的第二个for循环中,循环判断到B发现B为被访问,然后调用DFS函数,访问D,设为TRUE,然后发现B不存在未被访问的邻接顶点,所以退回到DFSTraverse函数的第二个for循环中,发现没有未被访问过的顶点了,所以遍历结束:ACDEB

从此过程可以看出第一个函数的作用就是如果我们的初始顶点无法完成遍历图中的所有顶点我们就可以通过循环遍历每一个顶点。

邻接矩阵法的DFS(BFS)序列唯一,邻接表法的不唯一

5.深度优先生成树

深度优先生成树:在深度遍历过程中我们可以得到一颗遍历树,称为深度优先生成树(生成森林)


邻接矩阵法的深度度优先生成树是唯一的,邻接表法的不唯一

6.遍历与连通性问题

如何通过遍历来判断该图的连通性?


上面是一个无向图:无论我们使用BFS还是DFS都能通过任何一个顶点访问到其他的顶点,所以他是一个连通图

所以我们有以下结论:在无向图中,在任意结点出发进行一次遍历(调用一次BFS或者DFS),若能访问全部结点,说明该图是连通的。


上面是一个非连通的无向图,我们在进行遍历(BFS或DFS)的时候为了遍历到每一个顶点,我们需要一个for循环对每一个顶点进行调用BFS或者DFS。

我们由此可以得到如下结论:在无向图中,调用遍历函数(BFS或DFS)的次数为连通分量的个数


如上面是一个有向图:我们从顶点B出发DFS可以遍历到任何顶点,但是能访问到所有顶点代表这个图是一个强连通图吗?答案当然是不是的,能遍历到所有顶点只能说明从某个顶点到另一个顶点有一条有向突击。

如果上图我们从顶点A开始遍历,我们则需要调用两次DFS,所以在有向图中,调用遍历函数(BFS或DFS)的次数为不是强连通分量的个数

无向图叫连图,有向图叫强连通

c++ 图的连通分量是什么_学习数据结构第五章:图(图的遍历操作)相关推荐

  1. 假设以邻接矩阵作为图的存储结构_学习数据结构第五章:图(图的存储方法)...

    第五章:图(图的存储方法) 1.邻接矩阵法 下面是一个无向图的表示,我们使用一个一维数组存放点集,使用一个二维数组存放边集 二维数组表示边:行号表示其实端点,列号表示结束端点,值表示该边是否存在,以及 ...

  2. 学习数据结构--第五章:图(图的应用)

    第五章:图(图的应用) 1.最小生成树 生成树:连通图包含全部顶点的一个极小连通子图 这里需要注意的是是一个极小连通子图 上面第一个是一个连通图,右侧两个图实它的生成树,他们包含了全部顶点且是极小连通 ...

  3. 中根遍历二叉查找树所得序列一定是有序序列_学习数据结构--第六章:查找(查找)

    第六章:查找 1.查找的基本概念 查找:在数据集合中寻找满足某种条件的数据元素的过程. 查找的结果 查找成功和查找失败 查找表:用于查找的数据集合,由同一种数据类型(或记录)的组成,可以是一个数组或链 ...

  4. 带父节点的平衡二叉树_学习数据结构--第四章:树与二叉树(平衡二叉树)

    第四章:树与二叉树(平衡二叉树) 1.平衡二叉树 平衡二叉树:AVL,任意结点的平衡因子的绝对值不超过一. 平衡因子:左子树高度 - 右子树高度 如上图二叉树,是否是平衡二叉树? 可以把所有结点的平衡 ...

  5. 《Go语言圣经》学习笔记 第五章函数

    <Go语言圣经>学习笔记 第五章 函数 目录 函数声明 递归 多返回值 匿名函数 可变参数 Deferred函数 Panic异常 Recover捕获异常 注:学习<Go语言圣经> ...

  6. 《SysML精粹》学习记录--第五章

    <SysML精粹>学习记录 第五章:用例图(Use Case Diagram) 用例图简介 用例图外框 小结 第五章:用例图(Use Case Diagram) 用例图简介   用例图可以 ...

  7. Programming Entity Framework-dbContext 学习笔记第五章

    ### Programming Entity Framework-dbContext 学习笔记 第五章 将图表添加到Context中的方式及容易出现的错误 方法 结果 警告 Add Root 图标中的 ...

  8. c++ 图的连通分量是什么_【自考】数据结构第五章图,期末不挂科指南,第9篇

    图的基本概念 首先,你要明确图是什么样子的,就是下面这个样子的 图的定义与术语 有向图和无向图 直接对比图就可以看出来,有向图和无向图的区别了,这个没有什么难的. 有向图和无向图的表示法有略微的区别, ...

  9. d3.js折线图_学习使用D3.js创建折线图

    d3.js折线图 by Sohaib Nehal 通过Sohaib Nehal 学习使用D3.js创建折线图 (Learn to create a line chart using D3.js) 使用 ...

最新文章

  1. teamcity plugin中读取js和css文件的方法
  2. 【camera-lidar】自动驾驶相机-激光雷达融合方案综述
  3. Angular单元测试里使用fixture.debugElement测试UI界面元素
  4. [Codevs] 1004 四子连棋
  5. 《人月神话》阅读笔记(三)
  6. 高德地图车机版缩放版_主图操作-开发指南-高德地图车机版 | 高德地图API
  7. 【疫情防控毕业设计源码】精品微信小程序社区疫情防控+后台管理系统|前后分离VUE[包运行成功]
  8. 分享:第十届“泰迪杯”数据挖掘挑战赛优秀作品--A1-基于深度学习的农田害虫定位与识别研究(一)
  9. 山东标梵讲解浅谈移动端开发技术
  10. 李开复给中国大学生的第五封信—你有选择的权利
  11. 基于vue2全家桶实现的,仿移动端QQ
  12. 史上最“犯贱”的十首情歌
  13. 阿尔兹海默症AD最新研究进展(2021年7月)
  14. 第 342 场力扣周赛
  15. vue 视频上传组件
  16. C基础 | 【05】(内存结构以及复合类型)
  17. 互联网营销师-淘宝直播运营
  18. Eclipse控制台中的中文输出乱码问题
  19. 2.管理者的分类与角色
  20. gpuimage123

热门文章

  1. [WPF]获取控件间的相对位置
  2. Vue开发中有着原声app效果的滚动的第三方插件better-scroll在github的上面的运用方法及地址
  3. java 的分类_java的基本类型(转)
  4. linux启动nifi指令,Nifi 组件脚本开发 - ExecuteScript 使用指南 (二)
  5. oracle tarmout off,如何关闭oracle rac选项 make rac_off
  6. vs code html table,vs Code 快速生成代码
  7. 大数据与数据挖掘考试题_2017-2019年全国Ⅱ卷高考考点数据分析(理综合)
  8. Task On The Board CodeForces - 1367D(思维)
  9. python image convert_从python运行imagemagick convert(控制台应用程序)
  10. 『ACM-算法-ST算法』信息竞赛进阶指南--区间最值问题的ST算法