广度优先遍历_第1页
广度优先遍历_第2页
广度优先遍历_第3页
广度优先遍历_第4页
广度优先遍历_第5页
已阅读5页,还剩78页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

广度优先遍历广度优先遍历广度优先遍历第8章图知识点图的逻辑结构及基本术语邻接矩阵和邻接表的存储结构和特点深度优先搜索和广度优先搜索两种遍历算法图的连通性和生成树的概念最短路径的含义及求最短路径的算法通过阅读报刊,我们能增长见识,扩大自己的知识面。广度优先遍历广度优先遍历广度优先遍历第8章图知识点通1第8章图知识点图的逻辑结构及基本术语邻接矩阵和邻接表的存储结构和特点深度优先搜索和广度优先搜索两种遍历算法图的连通性和生成树的概念最短路径的含义及求最短路径的算法第8章图知识点2难点图的遍历最小生成树最短路径要求熟练掌握以下内容:图的存储结构图的遍历算法了解以下内容:图的最小生成树和求最小生成树算法带权有向图的最短路径问题难点3第8章目录8-1图的定义和术语8-2图的存储表示8-3图的遍历8-4图的连通性8-5最短路径8-6有向无环图及其应用小结验证性实验8图子系统自主设计实验8最小生成树单元练习8第8章目录8-1图的定义和术语48-1

图的定义和术语

图(Graph)是一种比树形结构更复杂的非线性结构。在图形结构中,每个结点都可以有多个直接前驱和多个直接后继。8-1图的定义和术语8-1-1图的定义图(Graph)是由非空的顶点(Vertices)集合和一个描述顶点之间关系——边(Edges)的有限集合组成的一种数据结构。可以用二元组定义为:

G=(V,E)其中,G表示一个图,V是图G中顶点的集合,E是图G中边的集合。8-1图的定义和术语图(Graph)是一种比树形结5

图8-1无向图G1

图8-2有向图G2

V1V3V2V4V5V1V3V2V4

G1=(V,E)

V={v1,v2,v3,v4,v5};

E={(v1,v2),(v1,v4),(v2,v3),(v3,v4),(v3,v5),(v2,v5)}。(vi,vj)表示顶点vi和顶点vj之间有一条无向直接连线,也称为边。G2=(V,E)V={v1,v2,v3,v4}E={<v1,v2>,<v1,v3>,<v3,v4>,<v4,v1>}<vi,vj>表示顶点vi和顶点vj之间有一条有向直接连线,也称为弧。其中vi称为弧尾,vj称为弧头。图8-1无向图G1图8-2有向图68-1-2图的相关术语(1)无向图(Undigraph)在一个图中,如果每条边都没有方向(如图8-1),则称该图为无向图。(2)有向图(Digraph)在一个图中,如果每条边都有方向(如图8-2),则称该图为有向图。(3)无向完全图在一个无向图中,如果任意两顶点都有一条直接边相连接,则称该图为无向完全图。可以证明,在一个含有n个顶点的无向完全图中,有n(n-1)/2条边。8-1-2图的相关术语7(4)有向完全图在一个有向图中,如果任意两顶点之间都有方向互为相反的两条弧相连接,则称该图为有向完全图。在一个含有n个顶点的有向完全图中,有n(n-1)条弧。(5)稠密图、稀疏图我们称边数很多的图为稠密图;称边数很少的图为稀疏图。(6)顶点的度在无向图中:一个顶点拥有的边数,称为该顶点的度。记为TD(v)。(4)有向完全图8

在有向图中:

(a)一个顶点拥有的弧头的数目,称为该顶点的入度,记为ID(v);

(b)一个顶点拥有的弧尾的数目,称为该顶点的出度,记为OD(v);

(c)一个顶点度等于顶点的入度+出度,即:TD(v)=ID(v)+OD(v)。在图8-1的G1中有:

TD(v1)=2TD(v2)=3TD(v3)=3TD(v4)=2TD(v5)=2

在图8

-2的G2中有:

ID(v1)=1OD(v1)=2TD(v1)=3ID(v2)=1OD(v2)=0TD(v2)=1ID(v3)=1OD(v3)=1TD(v3)=2ID(v4)=1OD(v4)=1TD(v4)=2在有向图中:9

可以证明,对于具有n个顶点、e条边的图,顶点vi的度TD(vi)与顶点的个数以及边的数目满足关系:

(7)权图的边或弧有时具有与它有关的数据信息,这个数据信息就称为权(Weight)。ACBD58697(8)网——边(或弧)上带权的图称为网(Network)。如图8-3所示,就是一个无向网。如果边是有方向的带权图,则就是一个有向网。图8-3一个无向网示意可以证明,对于具有n个顶点、e条边的图,顶点vi的度10(9)路径、路径长度顶点vi到顶点vj之间的路径是指顶点序列vi,vi1,vi2,

…,vim,vj.。其中,(vi,vi1),(vi1,vi2),…,(vim,.vj)分别为图中的边。路径上边的数目称为路径长度。在图8-1的无向图G1中,v1→v4→v3→v5与v1→v2→v5是从顶点v1

到顶点v5

的两条路径,路径长度分别为3和2。(10)回路、简单路径、简单回路在一条路径中,如果其起始点和终止点是同一顶点,则称其为回路或者环。如果一条路径上所有顶点除起始点和终止点外彼此都是不同的,则称该路径为简单路径。在图8-1中,前面提到的v1到v5的两条路径都为简单路径。除起始点和终止点外,其他顶点不重复出现的回路称为简单回路,或者简单环。如图8

-2中的v1→v3→v4→v1。(9)路径、路径长度11(11)子图对于图G=(V,E),G’=(V’,E’),若存在V’是V的子集,E’是E的子集,则称图G’是G的一个子图。图8-4(a)表示出了图8-1无向图G1的一个子图,图8-4(b)表示出了图8-2有向图G2的一个子图。图8-4图G1和G2的两个子图示意(a)G1的子图(b)G2的子图V1V4V2V1V3V2V4V5(11)子图图8-4图G1和G2的两个子图12(12)连通图、连通分量在无向图中,如果从一个顶点vi到另一个顶点vj(i≠j)有路径,则称顶点vi和vj是连通的。任意两顶点都是连通的图称为连通图。无向图的极大连通子图称为连通分量。图8-5(a)中有两个连通分量,如图8

-5(b)所示。AEBFCDAEBFCD(a)无向图G3(b)G3的两个连通分量

图8-5无向图及连通分量示意(12)连通图、连通分量AEBFCDAEBFCD13(13)强连通图、强连通分量对于有向图来说,若图中任意一对顶点vi

和vj(i≠j)均有从一个顶点vi到另一个顶点vj有路径,也有从vj到vi的路径,则称该有向图是强连通图。有向图的极大强连通子图称为强连通分量。图8-2中有两个强连通分量,分别是{v1,v3,v4}和{v2},如图8-6所示。(14)生成树连通图G的一个子图如果是一棵包含G的所有顶点的树,则该子图称为G的生成树(SpanningTree)。在生成树中添加任意一条属于原图中的边必定会产生回路,因为新添加的边使其所依附的两个顶点之间有了第二条路径。若生成树中减少任意一条边,则必然成为非连通的。n个顶点的生成树具有n-1条边。V1V3V2V4图8-6有向图G2的两个强连通分量示意(13)强连通图、强连通分量(14)生成树V1V3V2V4图148-1-3图的基本操作图的基本操作有:(1)CreatGraph(G)输入图G的顶点和边,建立图G的存储。(2)DFSTraverse(G,v)在图G中,从顶点v出发深度优先遍历图G。(3)BFSTtaverse(G,v)在图G中,从顶点v出发广度优先遍历图G。

图的存储结构比较多。对于图的存储结构的选择取决于具体的应用和需要进行的运算。

下面介绍几种常用的图的存储结构。8-2

图的存储表示8-1-3图的基本操作图的存储结构比较多。对15

邻接矩阵是表示顶点之间相邻关系的矩阵。假设图G=(V,E)有n个顶点,即V={v0,v1,…,vn-1},则G的邻接矩阵是具有如下性质的n阶方阵:

1若(vi,vj)或<vi,vj>是E(G)中的边

A[i][j]=

0若(vi,vj)或<vi,vj>不是E(G)中的边若G是网,则邻接矩阵可定义为:

wij若(vi,vj)或<vi,vj>是E(G)中的边

A[i][j]=0或∞若(vi,vj)或<vi,vj>不是E(G)中的边其中,wij表示边(vi,vj)或<vi,vj>上的权值(如图7-3);∞表示一个计算机允许的、大于所有边上权值的数。邻接矩阵是表示顶点之间相邻关系的矩阵。16

无向图用邻接矩阵表示如图8-7所示;有向网用邻接矩阵表示如图8-8所示。图8-7一个无向图的邻接矩阵表示图8-8一个有向网的邻接矩阵表示无向图用邻接矩阵表示如图8-7所示;有向网用17

从图的邻接矩阵存储具有以下性质:(1)无向图的邻接矩阵一定是一个对称矩阵。因此,在具体存放邻接矩阵时只需存放上(或下)三角矩阵的元素即可。(2)对于无向图,邻接矩阵的第i行(或第i列)非零元素(或非∞元素)的个数正好是第i个顶点的度TD(vi)。(3)对于有向图,邻接矩阵的第i行(或第i列)非零元素(或非∞元素)的个数正好是第i个顶点的出度OD(vi)(或入度ID(vi))。(4)用邻接矩阵方法存储图,很容易确定图中任意两个顶点之间是否有边相连;但是,要确定图中有多少条边,则必须按行、按列对每个元素进行检测,所花费的时间代价很大。这是用邻接矩阵存储图的局限性。从图的邻接矩阵存储具有以下性质:18图的邻接矩阵存储的描述如下:#defineMAXLEN10typedefstruct{charvexs[MAXLEN];intedges[MAXLEN][MAXLEN];intn,e;}MGraph;建立一个有向图的邻接矩阵存储的算法如下:

图的邻接矩阵存储的描述如下:19voidCreateMGraph(MGraph&G){inti,j,k;charch1,ch2;printf("请输入顶点数和边数(输入格式为:顶点数,边数):\n");scanf("%d%d",&(G.n),&(G.e));printf("请输入顶点信息(顶点标志)每个顶点以回车作为结束:\n");for(i=0;i<G.n;i++){fflush(stdin);scanf("%c",&(G.vexs[i]));}for(i=0;i<G.n;i++)for(j=0;j<G.n;j++) G.edges[i][j]=0; //邻接矩阵初始化

printf("请输入每条边所对应的两个顶点(先输入弧尾,后输入弧头)!\n");for(k=0;k<G.e;k++){fflush(stdin);printf("请输入第%d条边的两个顶点标志(用逗号分隔):",k+1);scanf("%c,%c",&ch1,&ch2);for(i=0;ch1!=G.vexs[i];i++); for(j=0;ch2!=G.vexs[j];j++) G.edges[i][j]=1;} //将输入边对应的矩阵元素值设为1}voidCreateMGraph(MGraph&G)208-2-2邻接表邻接表(AdjacencyList)是图的一种顺序存储与链式存储结合的存储方法。邻接表表示法类似于树的孩子链表表示法。就是对于图G中的每个顶点vi,该方法将所有邻接于vi的顶点vj链成一个单链表,这个单链表就称为顶点vi的邻接表。再将所有点的邻接表表头放到数组中,就构成了图的邻接表。在邻接表表示中有两种结点结构,如图8-9所示。

图8-9邻接矩阵表示的结点结构8-2-2邻接表21

一种是顶点表的结点结构,它由顶点域(vertex)和指向第一条邻接边的指针域(firstedge)构成,另一种是边表结点,它由邻接点域(adjvex)和指向下一条邻接边的指针域(next)构成。对于网的边表需再增设一个存储边上信息(如权值等)的域(info),网的边表结构如图8-10所示。

图8-10网的边表结构一种是顶点表的结点结构,它由顶点域(vert22

图8-11给出无向图8-7对应的邻接表表示。

图8-11图8-7无向图的邻接表表示邻接表表示形式描述如下:图8-11给出无向图8-7对应的邻接表表示。图8-23#defineMAXLEN10//最大顶点数为10typedefstructedgenode{intadjvex; //邻接顶点的下标

structedgenode*next;//指向下一个邻接边结点的指针}EdgeNode; //定义边结点类型typedefstruct{VertexTypevertex;//顶点标志

EdgeNode*firstedge;//保存第一个边结点地址的指针}VertexNode; //定义顶点结点类型typedefstruct{VertexNodeadjlist[MAXLEN];//顶点数组

intn,e;//顶点数和边数}ALGraph;//定义邻接表的类型名ALGraph#defineMAXLEN1024voidCreateGraphAL(ALGraph&G)//有向图的邻接表存储算法{inti,k;EdgeNode*s;charArcTail,ArcHead; //保存弧尾、弧头标志

intTailPoi,HeadPoi;//保存弧尾、弧头下标

printf("请输入顶点数和边数(输入格式为:顶点数,边数):\n");scanf("%d%d",&(G.n),&(G.e)); //读入顶点数和边数

printf("请输入顶点信息(输入格式为:顶点号<CR>)以回车作为结束:\n");for(i=0;i<G.n;i++)//建立有n个顶点的顶点表

{scanf("%c",&(G.adjlist[i].vertex));//输入顶点信息

G.adjlist[i].firstedge=NULL;}//指向第一个边结点的指针设为空

printf("请输入边的信息(输入格式为:弧尾,弧头):\n"); for(k=0;k<G.e;k++) //建立边表

{scanf(“%c,%c”,&ArcTail,&ArcHead);for(i=0;i<G.n;i++)//查找弧尾和弧头顶点的下标

{if(G.adjlist[i].vertex==ArcTail) TailPoi=i; if(G.adjlist[i].vertex==ArcHead) HeadPoi=i;} s=newEdgeNode; //给新边结点开辟空间

s->adjvex=HeadPoi;//构造新边结点

s->next=G.adjlist[TailPoi].firstedge; G.adjlist[i].firstedge=s;}}voidCreateGraphAL(ALGraph&G25

若无向图中有n个顶点、e条边,则它的邻接表需n个头结点和2e个表结点。显然,在边稀疏(e<<n(n-1)/2)的情况下,用邻接表表示图比邻接矩阵节省存储空间,当和边相关的信息较多时更是如此。在无向图的邻接表中,顶点vi的度恰为第i个链表中的结点数;但在有向图中,第i个链表中的结点个数只是顶点vi的出度。如果要求入度,则必须遍历整个邻接表才能得到结果。有时,为了便于确定顶点的入度或以顶点vi为头的弧,可以建立一个有向图的逆邻接表,即对每个顶点vi

建立一个链接,以vi为弧头的弧的链表。例如图8-12所示为有向图G2(见图8-2)的邻接表和逆邻接表。若无向图中有n个顶点、e条边,则它的邻接表26

在建立邻接表或逆邻接表时,若输入的顶点信息即为顶点的编号,则建立邻接表的复杂度为O(n+e),否则,需要通过查找才能得到顶点在图中位置,则时间复杂度为O(n*e)。

图8-12图8-8有向网的邻接表和逆邻接表在建立邻接表或逆邻接表时,若输入的顶点信息即为顶点的278.2.3十字链表由于邻接表中通过某顶点查找以该顶点为弧尾的弧结点很方便,但是通过该顶点查找以其为弧头的弧结点则需要遍历整个邻接表;而逆邻接表在查找弧结点方面的优缺点则刚好与邻接表相反。为了通过某顶点能够方便地同时查找到以该顶点为弧尾和弧头的弧结点,可以将邻接表和逆邻接表合并,这样就构成了有向图的十字链表存储结构。十字链表的结点结构如图所示:图8-13十字链表的结点结构8.2.3十字链表图8-13十字链表的结点结构28十字链表的结点结构定义如下:#defineMAXLEN20typedefstructarcnode{intTailVex,HeadVex;//弧尾和弧头顶点的下标

structarcnode*TLink,*HLink;//指向同弧尾和同弧头的下一个弧结点的指针

intweight;//弧的权值信息}ArcNode; //定义弧结点的类型typedefstructvexnode{charvertex; //顶点标志

ArcNode*FirstIn,*FirstOut;//指向第一个弧头结点和弧尾结点的指针}VexNode;//定义顶点结点的类型typedefstruct{VexNodelist[MAXLEN];//顶点数组

intVexNum,ArcNum;//顶点数和弧数}OLGraph; //定义十字链表的类型十字链表的结点结构定义如下:29图8-8所示有向网的十字链表存储结构如图8-14所示。图8-14有向图的十字链表结构创建十字链表的算法如下:图8-8所示有向网的十字链表存储结构如图8-14所示。图8-30voidCreateOrthList(OLGraph&G){ArcNode*p;inti,j;charArcTail,ArcHead;charArcTail,ArcHead;printf("请输入有向网的顶点数和弧数:\n");scanf("%d%d",&G.VexNum,&G.ArcNum);printf("请依次输入%d个顶点(用回车分隔):\n",G.VexNum);for(i=0;i<graph->vexnum;i++){scanf("%c",&G.list[i].vertex); //输入顶点标志

G.list[i].FirstIn=NULL; //初始化弧结点指针

G.list[i].FirstOut=NULL;}printf("顶点数组创建成功!\n");voidCreateOrthList(OLGraph&G31voidCreateOrthList(OLGraph&G){ArcNode*p;inti,j;charArcTail,ArcHead;charArcTail,ArcHead;printf("请输入有向网的顶点数和弧数:\n");scanf("%d%d",&G.VexNum,&G.ArcNum);printf("请依次输入%d个顶点(用回车分隔):\n",G.VexNum);for(i=0;i<graph->vexnum;i++){scanf("%c",&G.list[i].vertex); //输入顶点标志

G.list[i].FirstIn=NULL; //初始化弧结点指针

G.list[i].FirstOut=NULL;}printf("顶点数组创建成功!\n");}voidCreateOrthList(OLGraph&G32

类似于有向图(或网)的十字链表存储结构,无向图(或网)还有一种邻接多重表(AdjacencyMultiList)的存储结构。虽然邻接表是无向图的一种很有效的存储结构,但是,在无向图的邻接表结构中,每条边都存在两个顶点,并且这两个顶点分别位于两个链表中,这给无向图的某些操作带来了不便。例如,在某些图的问题中需要对某条边进行某种操作,如插入或删除一条边等,此时都必须找到表示同一条边的两个边结点,并分别对其操作,这样显得比较繁琐。因此,在处理和边有关的大量问题中,更多的时候邻接多重表比邻接表显得更为合适。邻接多重表的每条边都用一个边结点表示,其边结点和顶点结点的结构分别如图8-15中的(a)(b)所示。图8-15邻接多重表的结点结构类似于有向图(或网)的十字链表存储结构,无向33邻接多重表的类型定义如下:#defineMAXLEN20typedefstructedgenode{intiVex,jVex; //边两端顶点的下标

structedgenode*iLink,*jLink;//指向具有相同端点的下一个边结点的指针

intweight;//边的权值信息}EdgeNode; //定义边结点的类型typedefstructvexnode{charvertex; //顶点标志

ArcNode*FirstEdge;//指向第一个邻接边结点的指针}VexNode; //定义顶点结点的类型typedefstruct{VexNodelist[MAXLEN]; //顶点数组intVexNum,EdgeNum;//顶点数和边数}AMLGraph; //定义邻接多重表的类型邻接多重表的类型定义如下:348-3

图的遍历

图的遍历(traversinggraph)是指从图中的某一顶点出发,对图中的所有顶点访问一次,而且仅访问一次。图的遍历是图的一种基本操作。由于图结构本身的复杂性,所以图的遍历操作也较复杂,主要表现在以下四个方面:(1)在图结构中,每一个结点的地位都是相同的,没有一个“自然”的首结点,图中任意一个顶点都可作为访问的起始结点。(2)在非连通图中,从一个顶点出发,只能够访问它所在的连通分量上的所有顶点,因此,还需考虑如何访问图中其余的连通分量。(3)在图结构中,如果有回路存在,那么一个顶点被访问之后,有可能沿回路又回到该顶点。(4)在图中,一个顶点可以和其它多个顶点相连,当这个顶点访问过后,就要考虑如何选取下一个要访问的顶点。8-3图的遍历图的遍历(traversing358-3-1深度优先搜索

深度优先搜索(Depth-FisrstSearch)遍历类似于树的先根遍历,是树的先根遍历的推广。假设初始状态是图中所有顶点未曾被访问,则深度优先搜索可从图中某个顶点发v出发,首先访问此顶点,然后任选一个v的未被访问的邻接点w出发,继续进行深度优先搜索,直到图中所有和v路径相通的顶点都被访问到;若此时图中还有顶点未被访问到,则另选一个未被访问的顶点作为起始点,重复上面的做法,直至图中所有的顶点都被访问。V1V5V2V4V8V3V6V7图8-16无向图G5

以图8-16的无向图G5为例,其深度优先搜索得到的顶点访问序列为:v1→v2→v4→v8→v5→v3

→v6

、→v7动画演示8-3-1深度优先搜索V1V5V2V4V8V3V6V7图836

显然,以上方法是一个递归的过程。为了在遍历过程中便于区分顶点是否已被访问,需附设访问标志数组visited[0:n-1],,其初值为FALSE,一旦某个顶点被访问,则其相应的分量置为TRUE。从图的某一点v出发,递归地进行深度优先遍历的过程如下面算法所示。

voidDFSTraverseM(MGraph*G)

{inti;

for(i=0;i<G->n;i++)

visited[i]=FALSE;//FALSE在c语言中定义为0,下同

for(i=0;i<G->n;i++)

if(!visited[i])

DFSM(G,i);

}显然,以上方法是一个递归的过程。为了在遍历过程37voidDFSM(MGraph*G,inti){intj;printf("\t\t深度优先遍历结点:结点%c\n",G->vexs[i]);visited[i]=TRUE; //TRUE在c语言中定义为1,以下同

for(j=0;j<G->n;j++)if(G->edges[i][j]==1&&!visited[j]) DFSM(G,j);}

在遍历时,对图中每个顶点至多调用一次DFSM函数,因为一旦某个顶点被标志成已被访问,就不再从它出发进行搜索。因此,遍历图的过程实质上是对每个顶点查找其邻接点的过程。其耗费的时间则取决于所采用的存储结构。当用二维数组表示邻接矩阵图的存储结构时,查找每个顶点的邻接点所需时间为O(n2),其中n为图中顶点数。voidDFSM(MGraph*G,inti)38

当以邻接表作图的存储结构时,找邻接点所需时间为O(e),其中e为无向图中边的数或有向图中弧的数。由此,当以邻接表作存储结构时,深度优先遍历的时间复杂度为O(n+e)。8-3-2广度优先搜索广度优先搜索遍历类似于树的按层次遍历。假设从图中某顶点vi出发,在访问了vi之后依次访问vi的各个未曾访问过的邻接点,然后分别从这些邻接点出发依次访问它们的邻接点,并使“先被访问的顶点的邻接点”先于“后被访问的顶点的邻接点”被访问,直至图中所有已被访问的顶点的邻接点都被访问到。若此时图中尚有顶点未被访问,则另选图中一个未曾被访问的顶点作起始点,重复上述过程,直至图中所有顶点都被访问到为止。当以邻接表作图的存储结构时,找邻接点所需时间为O(e39

对图8-16无向图进行广度优先遍历,首先访问v1

和v1的邻接点v2和v3,然后依次访问v2

的邻接点v4

和v5

及v3

的邻接点v6和v7,最后访问v4

的邻接点v8。由于这些顶点的邻接点均已被访问,并且图中所有顶点都被访问,由些完成了图的遍历。得到的顶点访问序列为:

v1→v2→v3→v4→v5→v6→v7→v8

和深度优先搜索类似,在遍历的过程中也需要一个访问标志数组。并且,为了顺次访问路径长度为2、3、…的顶点,需附设队列以存储已被访问的路径长度为1、2、…的顶点。V1V5V2V4V8V3V6V7图8-16无向图G5

动画演示对图8-16无向图进行广度优先遍历,首先访40

从图的某一点v出发,递归地进行广度优先遍历的过程如下面的算法所示。

voidBFSTraverseM(MGraph*G){inti;for(i=0;i<G->n;i++)visited[i]=FALSE;for(i=0;i<G->n;i++)if(!visited[i])BFSM(G,i);}从图的某一点v出发,递归地进行广度优先遍历的过程如下面41voidBFSM(MGraph*G,intk){inti,j;CirQueueQ;InitQueue(&Q);printf("广度优先遍历结点:结点%c\n",G->vexs[k]);visited[k]=TRUE;EnQueue(&Q,k);while(!QueueEmpty(&Q)){i=DeQueue(&Q);for(j=0;j<G->n;j++)if(G->edges[i][j]==1&&!visited[j]){printf("广度优先遍历结点:%c\n",G->vexs[j]);visited[j]=TRUE;EnQueue(&Q,j);}}}voidBFSM(MGraph*G,intk)428-4

图的连通性

分析上述算法,每个顶点至多进一次队列。遍历图的过程实质是通过边或弧找邻接点的过程,因此广度优先搜索遍历图和深度优先搜索遍历的时间复杂度是相同的,两者不同之处仅仅在于对顶点访问的顺序不同。

判定一个图的连通性是图的一个应用问题,我们可以利用图的遍历算法来求解这一问题。本节将讨论无向图的连通性问题,并讨论最小代价生成树等问题。8-4图的连通性分析上述算法,每个顶点至多438-4-1无向图的连通分量和生成树

在对无向图进行遍历时,对于连通图,仅需从图中任一顶点出发,进行深度优先搜索或广度优先搜索,便可访问到图中所有顶点。对非连通图,则需从多个顶点出发进行搜索,而每一次从一个新的起始点出发进行搜索过程中得到的顶点访问序列恰为其各个连通分量中的顶点集。例如,图8-17(a)是一个非连通图,按照图8-17(b)所示的邻接表进行深度优先搜索遍历

图8-17(a)非连通图图8-17(b)G6的邻接表AEBFCD8-4-1无向图的连通分量和生成树图8-17(a)非44

两次调用DFS过程(即分别从顶点A和D出发),得到的顶点访问序列为:ABFECE

这两个顶点集分别加上所有依附于这些顶点的边,便构成了非连通图G3的两个连通分量。

因此,要想判定一个无向图是否为连通图,或有几个连通分量,就可设一个计数变量count,初始时取值为0,在深度优先搜索算法循环中,每调用一次DFS,就给count增1。这样,当整个算法结束时,依据count的值,就可确定图的连通性了。

设E(G)为连通图G中所有边的集合,则从图中任一顶点出发遍历图时,必定将E(G)分成两个集合T(G)和B(G),其中T(G)是遍历图过程中历经的边的集合;B(G)是剩余的边的集合。显然,T(G)和图G中所有顶点一起构成连通图G的极小连通子图。两次调用DFS过程(即分别从顶点A和D出发45

按照8-1-2节的定义,它是连通图的一棵生成树,并且由深度优先搜索得到的为深度优先生成树;由广度优先搜索得到的为广度优先生成树。例如,图8-18(a)和(b)所示分别为图8-16连通图G5的深度优先生成树和广度优先生成树。图中虚线为集合B(G)中的边,实线为集合T(G)中的边。图8-18(a)G5的深度优先生成树图8-18(b)G5的广度优先生成树V1V5V2V4V8V3V6V7V1V5V2V4V8V3V6V7按照8-1-2节的定义,它是连通图的一棵生成树,并且由46

对于非连通图,通过这样的遍历,将得到的是生成森林。例如,图8-19(b)所示为图8-19(a)的深度优先生成的森林,它由三棵深度优先生成树组成。图8-19(a)非连通无向图G6图8-19(b)G6的深度优先生成树林JHKLMAICFGBDEJHKLMAICFGBDE对于非连通图,通过这样的遍历,将得到的是生成森林。例如478-4-2最小生成树1.最小生成树的基本概念由生成树的定义可知,无向连通图的生成树不一定是唯一的。连通图的一次遍历所经过的边的集合及图中所有顶点的集合就构成了该图的一棵生成树,对连通图的不同遍历,就可能得到不同的生成树。图8-20(a)、(b)和(c)所示的均为图8-16的无向连通图G5的生成树。V1V5V2V4V8V3V6V7V1V5V2V4V8V3V6V7V1V5V2V4V8V3V6V7

图8-17无向连通图G5的三棵生成树(a)(b)(c)8-4-2最小生成树V1V5V2V4V8V3V6V7V148

可以证明,对于有n个顶点的无向连通图,无论其生成树的形态如何,所有生成树中都有且仅有n-1条边。如果无向连通图是一个网,那么,它的所有生成树中必有一棵边的权值之和为最小的生成树,简称为最小生成树。最小生成树的概念可以应用到许多实际问题中。例如有这样一个问题:以尽可能抵的总造价建造城市间的通讯网络,把十个城市联系在一起。在这十个城市中,任意两个城市之间都可以建造通讯线路,通讯线路的造价依据城市间的距离不同而有不同的造价,可以构造一个通讯线路造价网络,在网络中,每个顶点表示城市,顶点之间的边表示城市之间可构造通讯线路,每条边的权值表示该条通讯线路的造价,要想使总的造价最低,实际上就是寻找该网络的最小生成树。可以证明,对于有n个顶点的无向连通图,无论其492.常用的构造最小生成树的方法(1)构造最小生成树的Prim算法假设G=(V,E)为一连通网,顶点集V={v1,v2,…,vn},E为网中所有带权边的集合。设置两个新的集合U和T,其中集合U用于存放G的最小生成树中的顶点,集合T存放G的最小生成树中的边。令集合U的初值为U={v1}(假设构造最小生成树时,从顶点v1出发),集合T的初值为T={}。

Prim算法的基本思想:从所有u∈U,v∈V-U的边中,选取具有最小权值的边(u,v),将顶点v加入集合U中,将边(u,v)加入集合T中,如此不断重复,直到U=V时,最小生成树构造完毕,这时集合T中包含了最小生成树的所有边。图8-21(a)所示的一个网,按照Prim方法,从顶点A出发,该网的最小生成树的产生过程如图8-21(b)、(c)、(d)、(e)、(f)所示。2.常用的构造最小生成树的方法50图8-21Prim算法构造最小生成树的过程示意图

AEBFCDAEBFCDAEBFCDAEBFCDAEBFCDAEBFCD(a)(b)(c)(d)(e)(f)6812141617515666668888812121214145图8-21Prim算法构造最小生成树的过程示意图AE51(2)构造最小生成树的Kruskal算法

Kruskal算法是一种按照网中边的权值递增的顺序构造最小生成树的方法。其基本思想是:首先选取全部的n个顶点,将其看成n个连通分量;然后按照网中边的权值由小到大的顺序,不断选取当前未被选取的边集中权值最小的边。依据生成树的概念,n个结点的生成树,有n-1条边,故反复上述过程,直到选取了n-1条边为止,就构成了一棵最小生成树。图8-22为Kruskal算法构造最小生成树的过程示意图。(2)构造最小生成树的Kruskal算法52图8-22Kruskal算法构造最小生成树的过程示意图

AEBFCDAEBFCDAEBFCDAEBFCDAEBFCDAEBFCD(a)(b)(c)(d)(e)(f)68121416175155666688885121255145图8-22Kruskal算法构造最小生成树的过程示意图538-5

最短路径

最短路径问题是图的又一个比较典型的应用问题。例如,某一地区的一个交通网,给定了该网内的n个城市以及这些城市之间的相通公路的距离,问题是如何在城市A和城市B之间找一条最近的通路。如果将城市用顶点表示,城市间的公路用边表示,公路的长度则作为边的权值,那么,这个问题就可归结为在网中,求点A到点B的所有路径中,边的权值之和最短的那一条路径。这条路径就称为两点之间的最短路径,并称路径上的第一个顶点为源点(Sourse),最后一个顶点为终点(Destination)。在不带权的图中,最短路径是指两点之间经历的边数最少的路径。例如在图8-23中,设V1为源点,则从V1出发的路径有(括号里为路径长度):8-5最短路径最短路径问题是图的又一个54V1到V2的路径有条:V1→V2(20)V1到V3的路径有条:V1→V3(15),V1→V2→V3(55)V1到V4的路径有条:V1→V2→V4(30),V1→V3→V4(45),V1→V2→V3→V4(85)V1到V5的路径有条:V1→V2→V3→V5(65),V1→V3→V5(25)选出V1到其它各顶点的最短路径,并按路径长度递增顺序排列如下:V1→V3(15),V1→V2(20),V1→V3→V5(25),V1→V2→V4(30)。

图8-23V1

出发的路径V1V5V2V4V3201035101530V1到V2的路径有条:V1→V2(20)图8-23V1出55

从上面的序列中,可以看出一个规律:按路径长度递增顺序生成从源点到其它各顶点的最短路径时,当前正生成的最短路径上除终点外,其它顶点的最短路径已经生成。迪杰斯特拉算法正是根据此规律得到的。迪杰斯特拉(Dijkstra)算法的基本思想:设置两个顶点集S和T,S中存放已确定最短路径的顶点,T中存放待确定最短路径的顶点。初始时S中仅有一个源点,T中含除源点外其余顶点,此时各顶点的当前最短长度为源点到该顶点的弧上的权值。接着选取T中当前最短路径长度最小的一个顶点v加入S,然后修改T中剩余顶点的当前最短路径长度,修改原则是:当v的最短路径长度与v到T中顶点之间的权值之和小于该顶点的当前最短路径长度时,用前者替换后者。重复以上过程,直到S中包含所有顶点为止,其过程如图8-24。

从上面的序列中,可以看出一个规律:按路径长度递增顺序56图8-24用迪杰斯特拉算法求有向图的最短路径过程

V1V5V2V4V3201035101530V1V5V2V4V32015V1V5V2V4V320101530V1V5V2V4V320101530V1V5V2V4V32010153010图8-24用迪杰斯特拉算法求有向图的最短路径过程V1V557迪杰斯特拉算法求最短路径的过程描述如下:#defineINFINITY99999#defineMAXLEN20typedefboolPathMatrix[MAXLEN][MAXLEN];typedefintShortPathTable[MAXLEN];voidShortestPath(MGraphG,intv0,PathMatrix&P,ShortPathTable&D){for(v=0;v<G.VexNum;v++){final[v]=False; //初始化S集合为空

for(w=0;w<G.VexNum;w++) //初始化最短路径矩阵P P[v][w]=False;//没有存储任何最短路径,所以全部元素均为False D[v]=G.arcs[v0][v];//初始的最短路径长度设置为v0到相应顶点弧的权值

if(D[v]<INFINITY)//若v0到顶点v的弧存在,则在P中设置好相应路径

{P[v][v0]=True;P[v][v]=True; }}迪杰斯特拉算法求最短路径的过程描述如下:58

D[v0]=0;//设置v0到自身的最短路径为0final[v0]=True; //将顶点v0加入到已选取顶点集合S中

for(i=1;i<G.VexNum;i++) { min=INFINITY;

for(w=0;w<G.VexNum;w++)//查找尚未加入到集合S中,并且带

//权路径长度最小的最短路径终

//点,该终点的下标保存在v中

if(!final[w]&&D[w]<min)//更新v的值

{v=w;min=D[w];} final[v]=True;//将顶点v加入到已选取顶点集合S中

for(w=0;w<G.VexNum;w++)//更新当前最短带权路径长度

//向量D及路径矩阵P if(!final[w]&&min+G.arcs[v][w]<D[w]) {D[w]=min+G.arcs[v][w]; P[w]=P[v]; P[w][w]=True;} }}D[v0]=0;59如图8-25所示的有向网中,从顶点A到其余各顶点的最短路径如表8-1所示。图8-25有向网表8-1顶点A到其它顶点的最短路径如图8-25所示的有向网中,从顶点A到其余各顶点的最短路径如60

以图8-25所示的有向网为实例,迪杰斯特拉算法求解该网中从源点A到其余各顶点最短路径的过程如图8-26所示。图8-26迪杰斯特拉算法的求解过程以图8-25所示的有向网为实例,迪杰斯特拉算618.6有向无环图及其应用一个不存在环的有向图称为有向无环图(DirectedAcyclineGraph,简称为DAG图)。有向无环图可用于描述某项工程的进行过程。除最简单的情况之外,几乎所有工程都可分为若干个称作活动的子工程,而这些子工程之间,通常受着一定条件的约束,如其中某些子工程的开始必须在另一些子工程完成之后。对于整个工程,人们往往最关心两个方面的问题:一是工程能否顺利进行;二是估算整个工程完成所必须的最短时间。其中前一个问题就是有向图能否拓扑排序的问题,后一个问题则和关键路径有关,下面分别讨论之。8.6有向无环图及其应用628.6.1拓扑排序拓扑排序是有向图的一个重要操作。在给定的有向图G中,若顶点序列满足下列条件:若在有向图G中从顶点到顶点有一条路径,则在序列中顶点必在顶点之前,便称这个序列为一个拓扑序列。求一个有向图拓扑序列的过程称为拓扑排序(TopologicalSort),实质上,拓扑排序由某个集合上的偏序关系得到该集合上的一个全序关系的过程。在离散数学课程中关于偏序关系和全序关系有如下定义:若集合S上的关系R是自反的、反对称的和传递的,则称R是集合S上的偏序关系。设R是集合S上的偏序(PartialOrder),如果对每个必有xRy或yRx,则称R是集合S上的全序关系。直观地看,偏序指集合中仅有部分成员之间可比较,而全序指集合中全体成员之间均可比较。8.6.1拓扑排序63

图8-27表示偏序和全序关系的有向图,假设图中弧<x,y>表示的关系为x≤y,则(a)表示偏序关系,(b)表示全序关系。(a)图之所以为偏序关系,是因为(a)图中存在顶点B和顶点C是不可比较的;由于关系是传递的,显然(b)图中的所有顶点之间均是可比较的,所以(b)图表示的是全序关系。一个表示偏序关系的有向图可用来表示一个流程图。它或者是一个施工流程图,或者是一个产品生产的流程图,再或者是一个数据流图(每个顶点表示一个过程)。图中每一条有向边表示两个子工程完成的先后次序。这种用顶点表示活动,用弧表示活动间优先关系的有向图称为顶点表示活动的网(ActivityOnVertex),简称AOV网。图8-27表示偏序和全序关系的有向图,假设64

在AOV网中,不应该出现有向环,因为存在环意味着某项活动应以自己为先决条件,如果这样,只能说明该活动是无法完成的,整个工程也无法再进行下去。因此,对AOV网应首先进行是否存在环的判定,如果不存在环,则可以进行拓扑排序得到相应的拓扑序列,反之,则不能得到拓扑序列。拓扑排序及检测是否存在环的步骤如下:(1)从有向图中选取一个入度为零的顶点将其输出,如果同时有多个顶点的入度为零,则从这些顶点中任选一个。(2)从图中删除选取的顶点以及以它为弧尾的所有弧。(3)不断重复以上两步,直至所有顶点全部输出,则所有顶点的输出顺序即为该图的拓扑序列;如果图中还有剩余顶点尚未输出,但是图中已找不到入度为零的顶点了,则说明图中存在环,不能进行拓扑排序。在AOV网中,不应该出现有向环,因为存在环意658.6.2关键路径

与AOV网相对应的是AOE网(ActivityOnEdge)。AOE网中是用边表示活动的,它是一种边带权值的有向无环图。AOE网中的顶点表示事件,弧表示活动,弧的权值一般表示执行活动需要的时间。通常,AOE网用来估算工程的完成时间。例如,图8-28是一个假想的有11项活动的AOE网。其中有9个事件A,B,C,D,E,F,G,H,I,每个事件表示在它之前的活动已经完成,在它之后的活动才可以开始。如A表示整个工程开始,I表示整个工程结束,E表示a4和a5都已经完成,a7和a8才可以开始。与每个活动相联系的数是执行该活动所需的时间。比如,a1活动需要6个时间单位,a2需要4个时间单位。8.6.2关键路径66图8-28AOE网

一般情况下,整个工程只有一个开始点(入度为零的顶点)和一个完成点(出度为零的顶点),开始点也称为源点,完成点也称为汇点。和AOV网不同,对AOE网有待研究的问题是:(1)完成整项工程至少需要多少时间?(2)网中的哪些活动是影响工程进度的关键?

图8-28AOE网一般情况下,整个工程只有67图8-29结点V的直接前驱Ri和直接后继Hi

由于AOE网中有些活动可以并行地进行,所以完成工程的最短时间是从开始点到完成点的最长路径的长度(这里所说的路径长度是指路径上各活动持续时间之和,不是路径上弧的数目)。从开始点到完成点之间路径长度最长的路径叫做关键路径(CriticalPath),关键路径上的所有活动称为关键活动。图8-29结点V的直接前驱Ri和直接后继Hi68

由于AOE网中的顶点代表事件,事件的发生有早有晚,假设顶点V的最早发生时刻为Early(V),最晚发生时刻为Late(V),则图8-29中的弧头顶点V的最早发生时刻:

即为所有弧尾顶点的最早发生时刻和对应弧的权值之和的最大值。图8-29中的弧尾顶点V的最晚发生时刻:即为所有弧头顶点的最晚发生时刻和对应弧的权值之差的最小值。规定起始点的最早发生时刻为0,所有顶点的最早发生时刻应从起始点开始,沿着弧的指向方向逐个顶点计算,直至完成点;计算出完成点的最早发生时刻后,规定其最晚发生时刻和最早发生时刻相等;然后从完成点开始,沿着弧的逆向方向逐个顶点计算所有顶点的最晚发生时刻。最终求出起始点的最晚发生时刻应该也为0。求出所有顶点的最早和最晚发生时刻如表8-2所示。由于AOE网中的顶点代表事件,事件的发生有早69

由于AOE网中的弧代表活动,活动的开始和完成均有早有晚,假设活动的最早开始时刻为,最晚开始时刻为,最早完成时刻为,最晚完成时刻为。由于AOE网中的弧代表活动,活动的开始和完成均70

此外,假定活动的持续时间为,富余时间为。由于事件发生,以该事件为弧尾的所有活动即可开始,所以活动的开始时刻取决于其弧尾顶点的发生时刻。当以某顶点为弧头的所有活动都完成,则该顶点代表的事件即可发生,所以活动的完成时刻决定了其弧头顶点的发生时刻。图8-30弧ai的弧尾R和弧头H

因此,图8-30中弧的各个相关属性值的计算有下列公式存在:此外,假定活动的持续时间为,富余时间为。图871

根据以上公式,计算所有活动的相关属性值如表8-3所示。富余时间为0的活动即为关键活动。表8-3所有活动的相关属性值根据以上公式,计算所有活动的相关属性值如表872

从表8-3可知,图8-28所示AOE网的关键路径由a1,a4,a7,a8,a10,a11这6个关键活动构成。分析关键路径的目的在于辨别哪些活动是关键活动,只有缩短关键活动的持续时间,才有可能缩短整个工程的工期。图8-31AOE网及其关键路径从表8-3可知,图8-28所示AO

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论