收藏 分享(赏)

ch9--图.ppt

上传人:j35w19 文档编号:6943030 上传时间:2019-04-28 格式:PPT 页数:288 大小:1.99MB
下载 相关 举报
ch9--图.ppt_第1页
第1页 / 共288页
ch9--图.ppt_第2页
第2页 / 共288页
ch9--图.ppt_第3页
第3页 / 共288页
ch9--图.ppt_第4页
第4页 / 共288页
ch9--图.ppt_第5页
第5页 / 共288页
点击查看更多>>
资源描述

1、数据结构,Data Structure,张先宜 13909696718 ,第9章 图,9.1 图的定义和基本概念 9.2 图的存储结构 4.3 链队列,图是一种直观、简洁、优美的数学工具。 这里所谓的“图”不是指图形、图像和数码照片。 图仅有两个构成要件顶点(vertex)和边(edge)。 但却能描述和表达自然界和人类社会生活中许多复杂的事物和关系。 例如: 人类一个群体中的“同学关系”和“认识关系”,计算机网络,web,城市交通网络,电力供应网络,城市自来水管网,神经网络等等,凡此种种都可以用图的形式来进行抽象表示。 数据结构中用它来描述和表示多对多的复杂数据结构。目前图已经被广泛应用到语

2、言学、逻辑学、物理、化学、通信、计算机科学、人工智能、数学的其它分支等众多学科和领域。,图结构相比前面介绍的其它数据结构更为复杂,线性结构中,结点之间是线性关系,一个结点最多只有一个直接前驱和一个直接后继;树型结构呈现明显的层次关系,每个结点最多只有一个双亲结点,但可以有多个孩子结点;而在图结构中,任意两个顶点之间都可能发生邻接关系,每个顶点都可能有多个直接前驱和多个直接后继。 本章先介绍图的定义和基本概念,然后讨论图结构在计算机中的两种最常用的存储形式。遍历算法是图结构最基本的运算,有两种遍历图的运算,即深度优先搜索遍历和广度优先搜索遍历,有关图结构上的许多运算都可借助于这两个运算的变化来实

3、现。不仅如此,这也是软件设计中常用的经典的运算,许多其它结构上的运算也可借助这两个运算的思想来实现。 在其后所介绍的内容是图结构的应用,也就是将图应用于某一具体问题的描述及其求解实现,通过对这些问题的背景、模型抽象、求解方法的实现等的理解,可进一步掌握图结构运用于实际问题的实现,从而为应用于实际问题奠定基础。,9.1.1 图的定义 图(Graph)G由两部分构成:顶点集合V和边(弧)的集合E, 记作 G =(V,E)。其中: 顶点(vertex)用来表示和描述各种对象。数据结构中用顶点表示数据元素,相当于前面各章中所介绍的元素、结点等,在图中都表示为一个顶点。 例如:一个图的顶点集合V=v1,

4、 v2, v3, v4。,边(edge)是顶点集V中的顶点对(偶对),表示两个顶点之间的关系。边可以加方向区分,这样边就可分为无向边和有向边(弧)。 无向边,简称边,由两个顶点的无序偶构成,用“(顶点1,顶点2)”形式表示,两个顶点位置可以互换。例:边e1=(v1, v2),表示由顶点v1 和v2构成一条边e1,交换顶点v1 和v2的位置,即变为(v2, v1),仍表示同一条边e1。 弧(arc),或有向边,由两个顶点的有序偶构成,用“”形式表示,顶点位置不能互换,交换后将表示另一条弧。例:弧e2=,表示由顶点v1 和v3构成一条弧e2。如果交互顶点v1 和v3的位置,即变为,则表示另外一条弧

5、。 有些教材规定无向边叫边,有向边叫弧,请读者阅读时注意。其中弧表示单向关系,而边表示相互关系,用离散数学中的术语来说,则分别表示为非对称关系和对称关系。如“A和B是同学”是相互关系,而“A认识B”则是单向关系,因为B不一定认识A。,图的图形化表示 上面定义的图(graph)是一种数学工具,而不是指图像或图形,这个工具只有顶点和边两种要素,但它却有着强大的形式化表达能力,在科学和工程上有许多的实际应用,是一种“简单、直观、优美、强大”的数学工具。图的图形化表示是指用图形方式来表示图的顶点和边。就是用图形上画的一个点,再加上顶点的名称表示图中的一个顶点;用图形上画出的连接两个顶点的连线表示图中的

6、一条边;如果是弧(有向边)就在连接顶点的线条上加箭头表示。,【例6.1】 已知图G1=(V1, E1),其中:V1=v1, v2, v3, v4, v5,E1=(v1,v2), (v1,v4), (v2,v3), (v3,v4), (v4,v5),画出G1的图形表示。 【解】 从给出的条件可知图有5个顶点,5条边。我们可以先画出5个顶点,再根据边集,在每条边关联的两个顶点之间画线条连接即可得到对应图形表示,如下图所示。,【例6.2】 已知图G2=(V2,E2),其中:V2=v1, v2, v3, v4,E2=, , , , ,画出图G2的图形表示。 【解】 由给出的条件可知G2有4个顶点,5条

7、边,且每条边都是弧(有向边)。与上题一样先画出4个顶点,再根据边集,在每条边关联的两个顶点之间画线条连接,且给线条加上箭头表示边的方向,如下图所示。,图G2的图形化表示,G2,我们在后面的介绍中经常会直接给出图的图形表示,以此表示一个图,而不再给出顶点集合和边的集合。,9.1.2 图的基本概念和术语,1. 无向图和有向图 无向图:每条边都是无向边。 有向图:每条边都是弧(有向边)。 混合图:既有有向边,又有无向边。(一般不讨论) 例:图G1是一个无向图;图G2是一个有向图。,2. 网络(带权图) 网(network)指边或弧上带有权值的图,也叫带权图。 网可以是无向的,也可以是有向的。 权值可

8、以表示两个顶点之间的“距离”。 计算机网络、通信网络、交通网络、供水网络等都可以抽象成这种网来表示。 【例】下图是一个网的实例。,G3,3. 子图 已知图G=(V,E),若另一个图G1=(V1,E1)是从图G中选取部分顶点和部分边(或弧)构成,即V1V,E1E,则称G1是G的子图。 例:下图中,G11、G12和G13是G1的子图;G21、G22和G23是图G2的子图。,(a) 图G1,子图示意,(b) 图G11,(c) 图G12,(d) 图G13,(e) 图G2,(f) 图G21,(g) 图G22,(h) 图G23,4. 邻接( adjacent ) 若两个顶点之间有边相连,则称这两个顶点邻接

9、(相邻的)。 无向图 G=(V,E),若顶点 u、w 之间有一条边 (u, w)E,则称顶点 u、w 互为邻接点。 有向图 G=(V,E),若顶点 u、w 之间有一条弧 E,则称 u 邻接 w,w邻接自(于)u。 例:无向图G1中,v1和v2、v3和v2、v5和v4等互为邻接顶点。有向图G2中,v1邻接v2、v3邻接v1、v2邻接v3等。,5. 顶点的度 度是指一个顶点关联的边的数量。对有向图还可以区分出入度和出度: 入度指射入顶点的边的数量。 出度指从顶点射出的边的数量。 那么,对有向图,顶点的度=入度+出度。,例: 无向图G1,顶点v1、v2和v3度为2,v4度为3,v5度为1。 有向图G

10、2中,顶点v1度为3,入度2,出度1;v2度为2,入度1,出度1;v3度为3,入度1,出度2;v4度为2,入度1,出度1。,由上面的讨论可知,度和边是相关的,由此我们可以推出以下结论: 无向图中:图的边数=图的顶点度数之和 / 2。 有向图中:入度之和=出度之和; 图的边数=入度之和=出度之和=图的顶点度数之和/2。,6. 路径和路径长度 路径(path):通俗地说就是从图中一个顶点出发,途经图中一些边和顶点能到达另外一个顶点,把途经的顶点依次排列成一个序列叫做路径。(数学定义有点繁杂,不再给出)。 对无向图,路径是双向可达的;对有向图,由于边的方向性,路径往往是单向可达的,可以区分出路径的起

11、点和终点。 路径对图中的某些顶点或边可以多次重复走过。 路径长度:一条路径上经过的边数(或弧数)。 计算路径长度时,对重复途经的同一条边要重复计数,比如途经同一条边3次,长度就要加3。,例:无向图G1中,(v1, v2, v3, v4, v5)是一条路径,长度为4;(v3, v2, v1, v4, v3, v2, v1)是一条路径,长度为6,其中边(v1, v2)和(v2, v3)重复走过2次,长度中计数4。 有向图G2中,(v1, v2, v3, v4)是一条路径,长度为3,起点v1,终点v4;(v3, v4, v1)是一条路径,长度为2,起点v3,终点v1;(v3, v1, v2, v3,

12、 v4, v1, v2)是一条路径,长度为6,起点v3,终点v2,边重复走过2次,长度中计数2。,7. 回路 路径上第一个顶点和最后一个顶点相同的闭合路径叫做回路,或叫环(loop)。回路中顶点或边也可以重复走过。 例:无向图G1中,(v1,v2,v3,v4,v1)是一个回路,长度4;(v1,v2,v3,v4,v5,v4,v1)是一个回路,长度6。 有向图G2中,(v1,v2,v3,v1)是一个回路,长度3;(v2,v3,v4,v1,v2)是一个回路,长度4。,8. 简单路径 路径中途径的顶点不重复叫简单路径。 例:无向图G1中, (v1, v2, v3, v4, v5)、(v1, v4, v

13、3, v2)等都是简单路径。 有向图G2中,(v1, v2, v3, v4) 、(v3, v4, v1, v2)等都是简单路径。,9. 简单回路 除了第一个顶点和最后一个顶点外,中间途经顶点不重复的闭合路径叫简单路径,或简单环。 例:无向图G1中,(v1,v2,v3,v4,v1)是一条简单回路。 有向图G2中,(v1,v2,v3,v1)、(v1,v2,v3,v4,v1)等都是简单回路。,10. 连通图 连通:图G 中,如果从顶点 u 到顶点 w 有路径,则称 u 和 w 是连通的。 连通图(connected graph):无向图G中,如果任意两个顶点之间都有路径,或是连通的,则称图G是连通图

14、。 例:图G1是一个连通图。,11. 连通分量 连通分量 (connected component):无向图中分割出来的极大连通子图。非连通图可视为由若干连通分量(连通子图)组成。 例:下图G是一个非连通图,由三个连通分量(连通子图)构成,每个连通分量都是一个连通图。,非连通图示例,G,【例9.3】求下图G的连通子图。 【解】这个图的顶点虽然画在一起,但细看就会发现它是一个非连通图,我们首先从中分割出一个极大连通分量G1,再在剩下部分中分割出第二个极大连通分量G2,最后剩下的单个顶点为一个连通分量G3。所以非连通图G由3个连通分量G1、G2和G3组成,如图所示。,非连通图分割连通分量示例,(a

15、)G,(b)G1,(c)G2,(d)G3,12. 强连通图 强连通图 (enhance-connected graph):有向图 G 中,若任意两个顶点之间都有路径,或连通的,则称 G 为强连通图。或:若有向图中任意两个顶点间可以互相到达,则称为强连通图。 例:下图G1和G2都是强连通图。 n个顶点的强连通图,至少要有n条边。,强连通图实例,(a)G1,(b)G2,13. 强连通分量 强连通分量(enhance-connected component):有向图中分割出来的极大连通子图。非强连通有向图由若干强连通分量构成。 例:下图所示有向图G是一个非强连通图,可分割出两个连通分量,如图(b)和

16、图(c)。,非强连通图分割连通分量实例,(a)G,(c)G2,(b)G1,14. 无向完全图 若无向图G中任意两个顶点之间都有一条边相连,称其为无向完全图。 n个顶点的无向完全图有n(n-1)/2条边。 例:下图为4个顶点的无向完全图,共有6条边。,4个顶点的无向完全图,15. 有向完全图 若有向图G中任意两个顶点之间都有一条弧(有向边)相连,称其为有向完全图。 n个顶点的有向完全图有n(n-1)条边。 例:下图为4个顶点的有向完全图,共有12条边。,4个顶点的有向完全图,16. (无向)树 若无向图连通并且无回路,则称为(无向)树。树还有如下几种等价的描述: 连通的无环图。 有n-1条边的连

17、通图。 有最少边的连通图。 例:下图为一棵(无向)树。,一棵无向树,17. 有向树 有向树指仅有一个顶点入度为0,其余顶点入度均为1的有向图。并称其中入度为0的顶点为其(有向)根。 例:下图为一棵有向树。,(b)一棵有向树,有的读者可能会想,第8章一章内容都在介绍树,这里怎么又出现了树呢?这里我们是从图论的角度给树下定义,可能您已注意到这个定义和第8章树的定义表述方式不同。 从图的角度看树只是一种特殊的图,因为树的特殊性且在计算机领域有诸多重要应用,所以我们专门用很大篇幅专门讨论了树结构。,18. 连通图的生成树 生成树 (spanning tree):一个 n 个顶点的连通图(或强连通图),

18、其生成树是它的一个极小的连通子图,它含有图中的全部顶点,但只有足以构成一棵树的 n-1 条边。 如果在一棵生成树上添加一条边,必定构成一个环,因为这条添加的边使得它依附的那两个顶点之间有了 2 条路经。 一棵有 n 个顶点的生成树,有且仅有 n-1 条边。 如果一个图有 n 个顶点和小于 n-1 条边,则它一定是非连通图。 如果它多于 n-1 条边,则一定有环。 但是,有 n-1 条边的图,不一定是生成树。,生成树是图论中一个重要的概念,在后续图的遍历算法和最小生成树等内容中都会涉及到生成树的问题。 例:下图(b)是(a)的一棵生成树。一个连通图往往可以产生多棵不同形态的生成树。,(a) 连通

19、图G,(b) 图G的一棵生成树,19. 非连通的生成森林 (spanning forest) 生成森林 (spanning forest):一个非连通图的生成森林由若干棵互不相交的树组成,含有图中的全部顶点,但是只有足以构成若干棵不相交的树的边(弧)。 例:下图(b)是(a)的生成森林,有2可树构成。通常生成森林时可有多种生成方法,生成森林的形态也会不同。,(a) 非连通图G,(b) 图G的生成森林,9.1.3 图的顶点编号,所谓顶点编号就是对图中顶点从1开始进行顺序编号,每个顶点对应一个序号。 图的各种算法在引用图的顶点时一般都用顶点的编号(序号)来代表顶点,而不是直接用顶点的元素值来区分。

20、 如下图。 因为图中所有顶点地位是平等的,如无特殊要求可以按任意次序编号。,为什么使用顶点编号呢? 首先,顶点元素值在不同问题中数据类型会不同,用编号代表顶点容易实现算法的通用性。 其次,顶点的元素值可能会重复,比如上图中元素b就是重复的,使用编号可以解决重复问题。 第三,图的很多算法中都会用到数组来保存顶点相关信息,使用编号可以直接对应到数组的下标,一是可以随机访问,二是某些情况下还可以用来降低数组的维数,因为数组下标就可以代表顶点。,假设有一个边数组E ,用邻接点来描述,存储上图中的边,如果直接存储顶点值,比如边(a,b),则数组元素就要2个分量。然而,用编号来存储用整型的一维数组就可以了

21、,比如,边(a,b)可表示为E2=1,边(b,c)可表示为E0=2,类似,(c,b)为E1=4,(b,d)为E3=6,(d,a)为E5=3等。有了编号后顶点元素可按编号顺序存储到一维数组中,需要用到元素值时,用编号获取即可。,9.2 图的存储结构,图结构在计算机中的存储形式。 图的结构较复杂,任意两个顶点间都可能存在联系,因而图的存储方法也很多,应根据具体的应用和施加的操作选择图的存储表示法。 邻接矩阵表示法 邻接链表(邻接表)表示法 邻接多重表表示法 十字链表表示法等 其中最为常用的是邻接矩阵和邻接表这两种形式。本节讨论这两种结构。,9.2.1 邻接矩阵表示,邻接矩阵是表示图中顶点之间邻接关

22、系的矩阵,即表示各顶点之间是否有边(弧)关系的矩阵。 对有n个顶点的图来说,用nn阶的邻接矩阵A表示,其中矩阵元素Aij表示顶点vi到vj之间是否有边或弧。,1. 图的邻接矩阵表示 这里的图指无向图或有向图,不含网(带权图)。矩阵A中,若顶点vi到vj之间有边或弧连接,则Aij=1;否则Aij=0。即:1 (vi,vj)E 或 EAij= 0 其它,【例1】无向图的邻接矩阵,对称矩阵,【例2】有向图的邻接矩阵,非对称矩阵,从邻接矩阵我们可以得出如下一些结论: 对无向图 邻接矩阵是对称的; 第i行或i列“1”的个数就是顶点vi的度; 图的边数=矩阵中“1”的个数/2; 对有向图 因为边的方向性,

23、邻接矩阵不一定对称; 第i行“1”的个数是顶点vi的出度,第i列“1”的个数是顶点vi的入度; 图的边数=矩阵中“1”的个数。,2. 网的邻接矩阵表示 网中每个边上都带有权值,邻接矩阵简单的用“1”和“0”来表示就不能描述权值。所以我们要对图的邻接矩阵进行改造。 因为网的每个边都有权值,我们就用这个权值作为邻接矩阵的元素; 如果两个顶点之间没有边或弧,我们用无穷大来代替(也可以用0来代替,视具体使用情况而定)。设顶点vi和vj之间有边(弧),权值为wij,则邻接矩阵元素Aij为: wij (vi,vj)E 或 EAij 或 0 否则,【例3】无向网的邻接矩阵,或者,对称矩阵,或者,【例4】有向

24、网的邻接矩阵,或者,网的邻接矩阵与图的邻接矩阵有类似的特性。 无向网的邻接矩阵是对称的;从中数出有效元素个数(非),再除2即网的边数。 有向网的邻接矩阵不一定对称;数出有效元素个数(非)即网的边数。 在实际实现时“”可以用计算机能接受的一个很大数来表示,只要能区分出有效的权值即可。,3.邻接矩阵存储结构描述 #define INF 65535 /定义无穷大,也可以是其它很大的数字 #define MaxVerNum 1000 /定义最大顶点个数,可根据需要定义最大顶点数 typedef char elementType; /定义图中顶点的数据类型,这里不妨设为char类型 typedef in

25、t cellType; /定义邻接矩阵中元素的数据类型,这里不妨设为int型/对无权图,1-相邻(有边),0-不相邻(无边)/对有权图,为边的权值,无边为无穷大。 typedef enum UDG, UDN, DG, DN GraphKind; /枚举图的类型-无向图,无向网,有向图,有向网,typedef struct GraphAdjMatrix /顶点数组,存放顶点元素的值elementType dataMaxVerNum; cellType AdjMatrixMaxVerNumMaxVerNum;/邻接矩阵,元素类型为cellTypeint VerNum; /顶点数int ArcNum

26、; /弧(边)数GraphKind gKind; /图的类型:0-无向图;1-无向网;/ 2-有向图;3-有向网/此项用以区分图的类型,为可选分量,可以取消。/此项也可以直接定义为整型,而不用枚举定义。 Graph; /图的类型名,图的邻接矩阵表示的优点: 非常直观,并且容易实现,编写算法也较简便,因而应用较广; 根据矩阵元素Aij=1或0,便于判定两个顶点之间是否有边(弧)相连; 计算顶点的度数,或有向图的入度、出度方便; 计算图的边数算法简单等。,图的邻接矩阵表示的缺点: 邻接矩阵事实上是一种顺序存储结构,具有顺序结构共有的缺点,比如:只能按最大空间需求申请内存空间、插入和删除顶点复杂等;

27、 空间复杂度高,n个顶点的图,存储邻接矩阵需要n2个单元,如果一个图的顶点数较多,但边(弧)数较少的话,邻接矩阵一样需要n2个存储单元,就太浪费存储空间; 统计图的边数算法虽然简单,用双重循环统计“1”的个数即可,但其时间复杂度为O(n2)。,9.2.2 邻接表表示,n个顶点的图的邻接链表由顺序存储的顶点表,及n个链式存储的边链表两部分组成。 回忆树和森林的“孩子链表”表示法。,1. 顶点表 顶点表是n个结点的顺序表。表中每个结点表示图中的一个顶点。 顶点表的结点结构:数据域+指针域。如图: 数据域存放顶点数据元素或顶点相关的其它信息; 指针域指向此顶点对应的边链表,即第一个邻接顶点。 本章后

28、面的描述中,顶点表采用数组(顺序表)实现。也可用链表实现。,2. 边链表 边链表保存顶点表中对应顶点的边的信息,即邻接顶点信息。 边链表结点结构 可由3个部分构成:邻接点域+信息域+指针域。如下图: 邻接点域保存邻接点信息,比如邻接点在顶点表中的编号等; 信息域这个域可选的,保存边的相关信息,比如在网中,可用来保存边的权值。 指针域指向下一条边(下一个邻接点)。,【例1】无向图的邻接链表表示,data,firstEdge,顶点表,边链表,【例2】有向图的邻接链表表示射出边表,data,firstEdge,顶点表,射出边链表,逆邻接链表 有向图中,使用顶点的射入边构成边链表。,data,firs

29、tEdge,顶点表,射入边链表,【例3】网的邻接链表表示,data,firstEdge,顶点表,边链表,3. 邻接表结构描述 #define INF 65535 /定义无穷大 #define MaxVerNum 1000 /定义最大顶点个数/定义图中顶点的数据类型 typedef char elementType;/定义eInfo的数据类型,即权值的数据类型 typedef int eInfoType; /枚举图的类型-无向图,无向网,有向图,有向网 typedef enum UDG, UDN, DG, DN GraphKind;,/定义边链表的结点结构 typedef struct eNod

30、e /邻接顶点信息,此处为顶点编号,从1开始int adjVer; /边链表中表示边的相关信息,比如表的权值eInfoType eInfo; /指向边链表中的下一个结点。 struct eNode* next; EdgeNode; /边链表结点类型,/定义顶点表的结点结构 typedef struct vNode elementType data; /存放图中顶点的数据值EdgeNode* firstEdge; /指向此顶点关联的第一条边的指针,即边链表的头指针/注意:fristEdge指针与边链表结点中的next指针类型相同 VerNode; /顶点表结点类型,/定义图的整体结构 typed

31、ef struct GraphAdjLinkList /顶点表,此为数组(顺序表),存放顶点信息/数组的元素为VerNode结构类型VerNode VerListMaxVerNum; int VerNum; /顶点数int ArcNum; /弧(边)数GraphKind gKind; /图的类型:0-无向图;1-无向网;2-有向图;3-有向网/此项用以区分图的类型,为可选分量,可以取消。/此项也可以直接定义为整型,而不用枚举定义。 Graph; /图的类型名,从邻接表我们可以得出如下一些结论: 通过邻接表我们可以求出图的边数,对无向图和网我们计数所有边链表的结点数,除以2即是边数;对有向图和网

32、通过邻接表或逆邻接表计数边链表结点数即是边数。 通过有向图的邻接表很容易求一个顶点的出度,计数对应边链表的结点个数即可;但求入度相对复杂; 利用逆邻接表很容易求一个顶点的入度,但求出度相对复杂。,邻接表的优点: 如果顶点表也采用链式结构存储,那么邻接表就可以动态申请内存,插入和删除顶点方便; 便于求图的边数; 顶点很多边很少的稀疏图空间效率较高,一个n个顶点,e条边的图(网),如果是无向图(网),需要n个顶点表结点和2e个边链表结点,如果为有向图(网),需要n个顶点表结点和e个边链表结点。,邻接表的缺点: 判断两个顶点之间是否有边(弧)相对复杂,比如vi和vj之间是否有边,需要先在顶点表中找到

33、vi结点,根据其firstEdge指针找到对应的边链表,然后搜索vj是否在此边链表上,在则有边(弧),不在则没有边(弧); 对有向图(网),邻接表求出度方便,但求入度麻烦,逆邻接表求入度方便,求出度麻烦。,9.2.3 图的创建和销毁,图通常有较多顶点,如果是网的话还涉及到边的权值。如果用键盘交互式创建图,键盘输入繁琐,且极容易出错,浪费大量宝贵时间。 如果图采用链式存储结构,创建图中途出错退出,还会造成内存泄漏。 这里介绍一种从文本文件读入图的数据创建图的方法,这样我们可以按照指定的格式,先从容地准备好数据,然后由程序自动读入数据来创建图。,1. 数据文件格式设计 这里数据用文本文件保存,文件

34、扩展名可自行指定,比如g8.grp,只要数据按文本文件格式读写即可。下面给出一种数据文件格式,其实读者可以自行设计图的数据文件格式。 标识行1:Graph 标识这是一个图的数据文件,这一行也可以不要。 标识行2:UDG、或UDN、或DG、或DN 这一行用来标识此图是无向图(UDG)、无向网(UDN)、有向图(DG)、还是有向网(DN)。, 顶点行 这一行将图中所有顶点列出,顶点之间用空格进行分割。这些顶点数据读出后存放到图的顶点数组中。 例如,下图所示的图的顶点行数据为:a b c d。 图的各种算法都是用顶点的编号来引用顶点的,所以这一行顶点的排列顺序是很重要的,顶点的排列顺序决定了顶点的编

35、号。比如上例中,顶点a、b、c、d对应的编号就为1、2、3、4。, 边数据行 一条边(弧)一行,边的2个顶点之间用空格分割。如果是网,每一行再加边的权值,也以空格分割。如果是无向图和无向网,每条边会重复一次。 【例】右图无向图的边的数据为: a b a c a d b a b c c a c b c d d a d c,【例】下图无向网边的数据为: a b 4 a c 5 a d 3 b a 4 b c 2 c a 5 c b 2 c d 6 d a 3 d c 6, 其它行 如果程序强大一点,还可以在文件中加注释行,允许出现空行等,当然这是非必须的。 例,下面2个图完整的数据文件为udg4.

36、grp和dn10.grp,2. 从数据文件创建邻接矩阵表示的图 参见实际实现代码。 3. 从数据文件创建邻接表表示的图 参见实际实现代码。 4. 图的销毁 以邻接矩阵存储的图,因为使用矩阵存储图的数据,不存在销毁(释放内存)问题。 但是以邻接表为存储结构的图,由于在创建图的过程中使用malloc()函数或new操作符动态申请了内存,当这个图不再需要时,必须手工释放动态申请的内存,否则造成内存泄漏。 顶点表不需销毁,只需逐条销毁边链表 下面给出一个销毁邻接表表示的图的程序。,void Graph:Destroy( ) EdgeNode *p,*u; /边链表结点指针int vID;for(vID

37、=1; vIDnext; /u指向下一个边结点delete(p); /删除当前边结点p=u; p=NULL;u=NULL;G.VerNum=-1; /标识图已经销毁 ,9.3 图的遍历算法及其应用,图的遍历 访问图中所有顶点一次且仅一次 图的常用遍历算法 深度优先搜索遍历(Depth First Search-DFS) 广度优先搜索遍历(Breadth_First Search-BFS),9.3.1 深度优先搜索遍历算法及其应用,深度优先搜索遍历(Depth First Search-DFS) 1. 基本深度遍历算法 假定图G是连通的,选定从顶点v0出发深度优先搜索遍历算法DFS(v0)描述如

38、下: : 访问v0visit(v0); 依次从v0的各个未被访问的邻接点出发执行深度遍历(DFS)。 虽然只有短短的两句话,但却将遍历过程描述得清清楚楚,这个算法的描述是递归的。 所谓“基本”是指这个算法只能遍历连通图,或一般图的一个连通分量。,此虚箭头表示在DFS(3)执行完毕后返回到DFS(2),即从顶点3返回到2,【例1】对下图进行深度优先遍历,DFS(2),DFS(1)包含如下两部分操作: (1)访问顶点1; (2)依次执行DFS(2)和DFS(8),DFS(1),此箭头表示是从遍历运算DFS(1)中调用DFS(2),即从顶点1直接转到2,DFS(3),在访问顶点3后,由于顶点3的邻接

39、点已全被访问,故DFS(3)执行到此结束,应返回到调用层,即返回到DFS(2),DFS(4),DFS(5),DFS(6),DFS(7),DFS(8),DFS(9),DFS(10),顶点访问序列:1,2,3,4,5,6,7,8,9,10,【思考问题】 DFS(1)访问顶点的次序是不是唯一的? 【答】不一定,本题中可能从DFS(1)先调用DFS(8),再调用DFS(2),由算法具体实现确定。 如果从DFS(7)开始遍历结果如何? 从图中任意指定的顶点开始遍历呢?,【例2】对下图进行深度优先遍历 顶点v0,v1,v2,v3,v4,v5,v6,v7的编号依次为:1,2,3,4,5,6,7,8,DFS(

40、1),顶点访问序列:1, 2, 4, 6, 5, 3, 7, 8。 即:v0, v1, v3, v5, v4, v2, v6, v7,DFS(2),DFS(4),DFS(6),DFS(5),DFS(3),DFS(7),DFS(8),【思考问题】 DFS(1)访问顶点的次序是不是唯一的? 【答】不一定,本题中可能从DFS(1)先调用DFS(3),再调用DFS(2),由算法具体实现确定。 如果从DFS(4)开始遍历结果如何? 从图中任意指定的顶点开始遍历呢?树可以进行深度优先搜索遍历吗? 【答】可以,树也是图,只是相对简单。 森林怎样进行深度优先搜索遍历吗? 【答】一棵树接着一棵树遍历,直至遍历完

41、森林中的每一棵树。,深度遍历生成树(DFS生成树) 对连通图和强连通图,保留深度优先遍历过程中途径的边(实例中有箭头标注的边),删除没有途径的边(没有箭头标注),则得到一棵树,叫做深度遍历生成树。 如果图是非连通,则会得到一个生成森林,每个连通分量对应一棵生成树。,执行DFS(1)的生成树,执行DFS(1)的生成树,图的深度遍历得到的顶点访问序列对应DFS生成树的先序遍历序列(以开始遍历的顶点为生成树的根结点)。 例1中皆为:1,2,3,4,5,6,7,8,9,10, 例2中皆为:顶点访问序列:1, 2, 4, 6, 5, 3, 7, 8。即:v0, v1, v3, v5, v4, v2, v

42、6, v7【思考问题】 对一个图,从不同的顶点开始遍历,得到的生成树是否相同? 【答】不一定相同。,2. 连通图(分量)的深度优先搜索遍历算法 【算法实现讨论】 (1) DFS(v)的第一句话为“访问顶点v” 即函数visit(v)的实现,大多场合是输出顶点的值,也可根据具体问题的需要来设计。 (2) DFS(v)的第二句话为“依次从顶点v的未被访问的邻接点出发进行深度遍历”。这涉及到以下内容: 顶点是否被访问的标识:设置访问标志数组visitedn+1,值为True表示已经访问,False表示未访问。假设图有n个顶点,其中visited0单元不用,是数组下标与顶点编号一致,都从1开始。,顶点

43、v的各邻接点的求解: 很显然,一个顶点的邻接点的求解取决于图的存储结构。例如,在用邻接矩阵存储图时,顶点vi的各邻接点在邻接矩阵的第i行中,因此可通过在该行中依次搜索非元素(或非0元素)来搜索所有邻接点;在用邻接表存储时,该顶点的邻接点全部在邻接表的第i个链表中,因此可通过依次取该链表中结点来实现所有邻接点的求解。 为使算法不受图的具体存储结构的影响,同时也为算法更清晰,下面的讨论更倾向于用如下两个不依赖于特定存储结构的邻接点函数来实现这一求解: firstAdj( G, v ):返回图G中顶点v的第一个邻接点。若不存在邻接点(编号),则返回0; nextAdj( G, v, w ):返回图G

44、中顶点v的邻接点中处于w之后的那个邻接点。若不存在这样的邻接点(编号),则返回0; 通过运用这两个函数,可依次求出一个顶点的所有邻接点。,从邻接点出发深度遍历的实现:可通过调用DFS算法来实现,也就是说,DFS算法是一个递归算法。 【DFS算法描述】 void Graph:DFS( int v ) /从编号v的顶点开始对图G进行深度优先搜索遍历int w;visit(v); /访问顶点vvisitedv=TRUE; /设置v已经访问标志w=firstAdj(v); /求出v的第一个邻接点,返回其编号给wwhile (w!=0) /当还存在邻接点时 /从没有访问过的邻接点出发继续深度遍历if (

45、visitedw=FALSE)DFS( w ); /递归深度优先遍历w=nextAdj( v, w ); /取下一个邻接点 ,3.一般图的(通用)深度优先搜索遍历算法 前面已经说过DFS( v )只能从指定顶点v出发遍历连通图,或一个连通分量。 如果图G是非连通的,只能遍历顶点v所在的连通分量,那么图G中就会剩下一些顶点未被访问。 解决办法是DFS( v )执行结束后,在未被访问的顶点中选一个再次执行DFS,反复执行这个过程,直到所有顶点都被访问。 上述过程每执行一次,就遍历一个连通分量。 现在的问题是:需要选择多少次起点?显然,这取决于具体的图,有多少个连通分量就会选择多少次,因而只能在算法

46、中通过条件加以判断来实现。 显然,在启动遍历算法之前应初始化各顶点的访问标志为False。,综上讨论,可得遍历图的完整算法如下: 【通用深度优先搜索遍历算法】 void Graph:Traverse ( ) int i;/初始化各顶点的访问标志为Falsefor (i=1; i=n; i+)visitedi=False; for (i=1; i=n; i+) /循环选择未被访问的顶点i,调用DFSif (visitedi=False) DFS( i ); /每次循环,遍历一个连通分量 这个算法对连通图(网)、非连通图(网)都是适用的,如果G是连通图(网)在第一次调用DFS时就访问了全部顶点,不

47、会有第二次调用,所示可成为通用深度遍历算法。,4. 连通图(分量)DFS的实现(参考) (1) 基于邻接矩阵的实现 void Graph:DFS(int v) visit(v); /访问顶点v,如:cout=1) ,(2) 基于邻接表的实现 void Graph:DFS( int v ) visit(v); /访问顶点v,如:coutadjVer ) / p-adjVer 为v的邻接点编号DFS( p-adjVer ); /递归访问顶点v的邻接点p=p-next; /p指向v的下一个邻接点 ,5. 任意图(通用)DFS的实现(参考) 此算法对邻接矩阵、邻接表表示相同。 void Graph:D

48、fsTraverse(int v) int vID;for(vID=1;vID=VerNum;vID+) /访问标记数组初始化visitedvID=false; DFS(v); /遍历指定的第一个连通分量,起点为vfor( vID=1;vID=VerNum;vID+ ) /再依次遍历图中其它的连通分量if(!visited vID )DFS( vID ); /每次执行DFS(vID)遍历一个连通分量 ,6. DFS算法分析 基本操作为访问顶点visit(v),从指定顶点v开始,要搜索v的所有邻接点,然后递归遍历,这是最耗时的,且时间复杂度与图的存储结构相关。 (1) 邻接矩阵存储 搜索某个顶点 i 的邻接点,需要访问邻接矩阵中 i 行的所有元素进行判断,所有顶点都要完成此操作,假定图的定点数为n,所以算法时间复杂度为:O(n2)。 (2) 邻接表存储 搜索顶点i的邻接点,即需要搜索i的边链表各个结点。虽然各个顶点边链表长度不同,但搜索总次数取决于图的边数e(无向图和网为2e),所以时间复杂度为:O(n+e)。,

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 生活休闲 > 社会民生

本站链接:文库   一言   我酷   合作


客服QQ:2549714901微博号:道客多多官方知乎号:道客多多

经营许可证编号: 粤ICP备2021046453号世界地图

道客多多©版权所有2020-2025营业执照举报