《《数据结构教学课件》第09章.ppt》由会员分享,可在线阅读,更多相关《《数据结构教学课件》第09章.ppt(115页珍藏版)》请在三一办公上搜索。
1、第9章 图,图的基本概念图的存储结构图的实现图的遍历最小生成树最短路径拓扑排序 关键路径,主要知识点,教学计划编排问题 一个教学计划包含许多课程,在教学计划包含的许多课程之间,有些课程之间有先修和后续的关系,有些课程可以任意安排次序。,教学计划编排问题(图形结构)各课程之间的次序关系可用一个称作图的数据结构来表示,如课程之间优先关系有向图。有向图中的每个顶点表示一门课程,如果从顶点vi到vj之间存在有向边,则表示课程i必须先于课程j进行。,课程之间优先关系的有向图,9.1 图,1.图的基本概念,图是由顶点集合及顶点间的关系集合组成的一种数据结构。图G的定义是:G=(V,E),其中,V=x|x某
2、个数据元素集合E=(x,y)|x,yV或E=x,y|x,yV并且Path(x,y)其中,(x,y)表示从 x到 y的一条双向通路,即(x,y)是无方向的;Path(x,y)表示从 x到 y的一条单向通路,即Path(x,y)是有方向的。问题:图的特点,图有许多复杂结构,本课程只讨论最基本的图,因此,本章讨论的图中不包括存在从自身到自身的边的图,以及多条边的图,带自身环的图,多重图,基本术语:,(1)顶点和边:图中的结点一般称作顶点,图中的第i个顶点记做vi。两个顶点vi和vj相关联称作顶点vi和vj之间有一条边,图中的第k条边记做ek,ek=(vi,vj)或vi,vj。,(2)有向图和无向图:
3、在有向图中,顶点对x,y是有序的,顶点对x,y称为从顶点x到顶点y的一条有向边,有向图中的边也称作弧;在无向图中,顶点对(x,y)是无序的,顶点对(x,y)称为与顶点x和顶点y相关联的一条边。,(3)完全图:在有n个顶点的无向图中,若有n(n-1)/2条边,即任意两个顶点之间有且只有一条边,则称此图为无向完全图;在有n个顶点的有向图中,若有n(n-1)条边,即任意两个顶点之间有且只有方向相反的两条边,则称此图为有向完全图,无向完全图,不是无向完全图,有向完全图,不是有向完全图,(4)邻接顶点:在无向图G中,若(u,v)是E(G)中的一条边,则称u和v互为邻接顶点,并称边(u,v)依附于顶点u和
4、v;在有向图G中,若u,v是E(G)中的一条边,则称顶点u邻接到顶点v,顶点v邻接自顶点u,并称边u,v和顶点u和顶点v相关联。,(5)顶点的度:顶点v的度是与它相关联的边的条数,记作TD(v)。有向图:顶点的度=入度+出度。入度ID(v)定义为以顶点v为终点的有向边的条数;出度OD(v)定义为以顶点v为起点的有向边的条数;无向图:TD(v)=ID(v)=OD(v),(6)路径:在图G=(V,E)中,若从顶点vi出发有一组边使可到达顶点vj,则称顶点vi到顶点vj的顶点序列为从顶点vi到顶点vj的路径.,从顶点0到顶点3的路径?如图G1中:,顶点3,顶点0,顶点2,顶点0,顶点3,(7)权:有
5、些图的边附带有数据信息,这些附带的数据信息称为权。带权的图也称作网络或网。,(8)路径长度:对于不带权的图,一条路径的路径长度是指该路径上的边的条数;对于带权的图,一条路径的路径长度是指该路径上各个边权值的总和。,(9)子图:设有图G1=V1,E1和图G2=V2,E2,若V1V2,且E1 E2,则称图G2是图G1的子图。,图G2,图G1,(10)连通图和强连通图:在无向图中,若从顶点vi到顶点vj有路径,则称顶点vi和顶点vj是连通的。如果图中任意一对顶点都是连通的,则称该图是连通 图。在有向图中,若对于任意一对顶点vi和顶点vj(vivj)都存在路径,则称图G是强连通图。,不是强连通图,强连
6、通图,连通图,(11)生成树:一个连通图的最小连通子图称作该图的生成树。有n个顶点的连通图的生成树有n个顶点和n-1条边。生成树一般是对无向图来讨论。,图G2的生成树,(12)简单路径和回路:若路径上各顶点v1,v2,vm,互不重复,则称这样的路径为简单路径;若路径上第一个顶点v1与最后一个顶点vm重合,则称这样的路径为回路或环。,数据集合:由一组顶点集合vi和一组边ej集合组成。当为带权图时每条边上权wj还构成权集合wj。操作集合:(1)初始化Initiate(G,n)(2)插入顶点 InsertVertex(G,vertex)(3)插入边InsertEdgeG,v1,v2,weight)(
7、4)删除边DeleteEdge(G,v1,v2)(5)删除顶点DeleteVertex(G,vertex)(6)取第一个邻接顶点GetFirstVex(G,v)(7)取下一个邻接顶点GetNextVex(G,int v1,v2)(8)遍历DepthFirstSearch(G),2.图的抽象数据类型,9.2 图的存储结构,图的存储结构主要有邻接矩阵和邻接表两种。,1.图的邻接矩阵存储结构,假设图G=(V,E)有n个顶点,即V=v0,v1,vn-1,E可用如下形式的矩阵A描述,对于A中的每一个元素aij,满足:,由于矩阵A中的元素aij表示了顶点vi和顶点vj之间边的关系,或者说,A中的元素aij
8、表示了顶点vi和顶点vj(0jn-1)的邻接关系,所以矩阵A称作邻接矩阵。,无向图的邻接矩阵一定是对称矩阵,有向图的邻接矩阵一般是非对称矩阵,其中V表示了图的顶点集合,A表示了图的邻接矩阵,对于带权图,邻接矩阵定义为:,带权图及其邻接矩阵,其中V表示了图的顶点集合,A表示了图的邻接矩阵。对于带权图,邻接矩阵第i行中所有0aij的元素个数等于第i个顶点的出度,邻接矩阵第j列中所有0aij的元素个数等于第j个顶点的入度。,2.图的邻接表存储结构,当图的边数少于顶点个数且顶点个数值较大时,图的邻接nn矩阵的存储问题就变成了稀疏矩阵的存储问题,此时,邻接表就是一种较邻接矩阵更为有效的存储结构。,顶点信
9、息,顶点在数组中的下标,顶点的邻接顶点在数组中下标构成单链表的头指针,数组的data域存储图的顶点信息;sorce域存储该顶点在数组中的下标序号;adj域为该顶点的邻接顶点单链表的头指针。第i行单链表中的dest域存储所有起始顶点为vi的邻接顶点vj在数组中的下标序号,next域为单链表中下一个邻接顶点的指针域。如果是带权图,单链表中需再增加cost域,用来存储边的权值wij。,9.3 图的实现,1.邻接矩阵存储结构下图操作的实现,设图G=(V,E),V是顶点集可以用顺序表表示;,E是边集,刻画了顶点之间的关系,用邻接矩阵表示;,边的数量,当做整体,用一个变量表示,结构体变量,先定义相应的结构
10、体,事先确定顶点个数MaxVertices,结构体定义,#include SeqList.h struct SeqList Vertices;int edgeMaxVerticesMaxVertices;int numOfEdges;,一个图G可以用一个AdjMGraph类型的结构体变量或指针来表示。定义语句为:AdjMGraph G,*G1;,typedef,AdjMGraph,void Initiate(AdjMGraph*G,int n)int i,j;ListInitiate(/*边的条数置为0*/,初始化G,对于给定的图G,给定一个AdjMGraph类型的结构体变量或指针。定义语句为
11、:AdjMGraph G;,初始化顺序表成员,初始化顶点关系成员,初始化边数成员,给定一个AdjMGraph类型的结构体变量或指针。定义语句为:AdjMGraph*G;,void InsertVertex(AdjMGraph*G,DataType vertex)ListInsert(,在给定的图G中插入顶点,给定一个AdjMGraph类型的结构体变量或指针。语句为:AdjMGraph G;,给定一个AdjMGraph类型的结构体变量或指针。语句为:AdjMGraph*G;,给定顶点,顶点插入操作,就是对AdjMGraph类型的结构体变量中的成员Vertices插入数据,也就是顺序表的插入操作,
12、G-Vertices.size,在给定的图G中插入边,给定一个AdjMGraph类型的结构体变量或指针。语句为:AdjMGraph G;,给定一个AdjMGraph类型的结构体变量或指针。语句为:AdjMGraph*G;,给定边,边插入操作,就是对AdjMGraph类型的结构体变量中的成员edges插入数据,也就是修改二维数组操作,AdjMGraph类型的结构体变量中的成员edges插入数据导致边数的增加,因此还需要修改成员Numofedges的值,给定边两个邻接顶点在顺序表中的位置标号,void InsertEdge(AdjMGraph*G,int v1,int v2,int weight)
13、if(_)printf(参数v1或v2越界出错!n);exit(1);G-edgev1v2=weight;G-numOfEdges+;,插入边操作,v1=G-Vertices.size|v2=G-Vertices.size,void DeleteEdge(AdjMWGraph*G,int v1,int v2)if(v1=G-Vertices.size|v2=G-Vertices.size|v1=v2)printf(参数v1或v2越界出错!n);exit(1);if(_)printf(该边不存在!n);exit(0);G-edgev1v2=MaxWeight;G-numOfEdges-;,删除边
14、操作,G-edgev1v2=MaxWeight|v1=v2,int GetFirstVex(AdjMGraph G,int v)int col;if(v)printf(参数v1越界出错!n);exit(1);for(col=0;col 0,取第一个邻接顶点,大于0并且小于MaxWeight?,int GetNextVex(AdjMGraph G,int v1,int v2)int col;if(v1=|v2=)printf(参数v1或v2越界出错!n);exit(1);for(col=v2+1;col 0,大于0并且小于MaxWeight?,取下一个邻接顶点,2.邻接矩阵图操作的测试,例9-1
15、 以下图所示的带权有向图为例编写测试邻接矩阵图的程序。,边的权值,边的起点,边的终点,顶点存储在一个字符数组中,边信息存储在一个结构数组中,图的创建函数设计如下:边信息结构体定义typedef struct int row;/行下标 int col;/列下标 int weight;/权值 RowColWeight;,创建该图,对一个AdjMGraph类型的结构变量插入顶点和边,void CreatGraph(AdjMGraph*G,DataType V,int n,RowColWeight E,int e)/*在图G中插入n个顶点信息V和e条边信息E*/int i,k;Initiate(G,n
16、);/*初始化n个顶点的图*/for(i=0;i n;i+)InsertVertex(G,Vi);/*顶点插入*/for(k=0;k e;k+)InsertEdge(G,Ek.row,Ek.col,Ek.weight);/*边插入*/,图的创建函数,图存放的AdjMGraph类型的指针变量,顶点和顶点数,边信息和边数,测试程序设计如下:#include#include typedef char DataType;#define MaxSize 100#define MaxVertices 10#define MaxEdges 100#define MaxWeight 10000#include
17、 AdjMGraph.h#include AdjMGraphCreate.h,void main(void)AdjMWGraph g1;DataType a=A,B,C,D,E;RowColWeight rcw=0,1,10,0,4,20,1,3,30,2,1,40,3,2,50;int n=5,e=5;int i,j;CreatGraph(,2.邻接表存储结构下图操作的实现,三个成员:data,source,单链表头指针adj,两个成员:下标dest,下一个结点指针next,2.邻接表存储结构下图操作的实现,邻接表的存储结构typedef struct Nodeint dest;/*邻接边的
18、弧头顶点序号*/struct Node*next;Edge;/*邻接边单链表的结点结构体*/typedef structDataType data;/*顶点数据元素*/int sorce;/*邻接边的弧尾顶点序号*/Edge*adj;/*邻接边的头指针*/AdjLHeight;/*数组的数据元素类型结构体*/,typedef struct AdjLHeight aMaxVertices;/*邻接表数组*/int numOfVerts;/*顶点个数*/int numOfEdges;/*边个数*/AdjLGraph;/*邻接表结构体*/讨论:图操作的实现方法,一个邻接表用一个AdjLGragph类
19、型的指针变量表示,定义语句为:AdjLGraph*G;,0,1,2,i,MaxVertices,3.插入第i个顶点,i?,AdjLGraph*G,ai,data,0,1,2,v1,MaxVertices,4.插入边,v1,v2是否在numOfVerts范围内?,已知顶点v1和v2,adj,p,v2,修改边数,0,1,2,v1,MaxVertices,5.删除边,v1,v2是否在numOfVerts范围内?,已知顶点v1和v2,adj,查找v2?,curr,只要curr存在且其dest值!=v2则curr下移一个,一般需要记录curr的前一个,引入pre,curr,pre,找完,分为三种情况.在
20、第一个.不是第一个.其他,0,1,2,v,MaxVertices,6.取第一个邻接顶点,v是否在numOfVerts范围内?,已知顶点v,adj,邻接表存储结构下的图的定义和操作测试参见具体程序:GraphTest.c,9.4 图的遍历,1.图的深度和广度优先遍历算法,图的遍历是访问图中的每一个顶点且每个顶点只被访问一次.图遍历方法深度优先遍历广度优先遍历算法设计需要考虑三个问题:算法的参数要指定访问的第一个顶点;要考虑遍历路径可能出现的死循环问题;要使一个顶点的所有邻接顶点按照某种次序被访问。,一、图的深度优先遍历算法,图的深度优先遍历算法是遍历时深度优先的算法;图的深度优先遍历是指在图的所
21、有邻接顶点中,每次都在访问 完当前顶点之后,接着首先访问当前顶点的第一个邻接顶点。图的深度优先遍历算法是一个函数递归调用算法。,开始,选择初始顶点v,0,取v的第一个邻接顶点w,w访问?,取v的邻接顶点w的下一个邻接顶点w,结束,0,w存在否,按深度优先遍历访问w,1,访问和标记v已访问,连通图的深度优先遍历算法为:,对于例9-8所示的有向图,顶点A作为初始访问结点,深度优先遍历访问顶点次序为:A B D C E,同一个路径顶点优先,二、图的广度优先遍历算法,图的广度优先遍历算法是一个分层搜索的过程,需要一个队列以保持访问过的顶点的顺序,以便按访问过的顶点的顺序来访问这些顶点的邻接顶点。连通图
22、的广度优先遍历算法为:,图的广度优先遍历是指从指定的顶点开始,按照到该顶点 路径长度由短到长的顺序,依次访问图中的其余顶点。,开始,选择初始顶点v,顶点入队列Q,Q的队头元素u出队,Q空否,0,寻找u的第一个邻接顶点w,w访问?,取u的邻接顶点w的下一个邻接顶点w,1,结束,0,w存在否,访问和标记w已访问,0,1,访问和标记v已访问,对于例9-8所示的有向图,顶点A作为初始访问结点,广度优先遍历访问顶点次序为:A B E D C问题:图的(深度、广度)遍历和生成树的关系?,同一个顶点的邻接顶点优先,三、非连通图的遍历,对于非连通图,从图的任意一个顶点开始深度或广度优先遍历并不能访问图中的所有
23、顶点。只能访问和初始顶点连通的所有顶点。但是,每一个顶点都作为一次初始顶点进行深度优先遍历或广度优先遍历,并根据顶点的访问标记来判断是否需要访问该顶点,就一定可以访问非连通图中的所有顶点。,void DepthFSearch(AdjMWGraph G,int v,int visited,void Visit(DataType item)int w;Visit(G.Vertices.listv);visitedv=1;w=GetFirstVex(G,v);while(w!=-1)if(!visitedw)DepthFSearch(G,w,visited,Visit);w=GetNextVex(G
24、,v,w);,2.图的深度和广度优先遍历函数实现,一、连通图深度优先遍历,二、非连通图的深度优先遍历,void DepthFirstSearch(AdjMWGraph G,void Visit(DataType item)int i;int*visited=(int*)malloc(sizeof(int)*);for(i=0;i;i+)visitedi=0;for(i=0;i;i+)if(!visitedi)DepthFSearch(G,i,visited,Visit);free(visited);,思想:对图中每一个顶点作为初始顶点进行深度优先遍历,为了避免访问过的顶点重新被访问,引入标记指
25、针,对图中顶点进行循环,三、连通图的广度优先遍历,#include SeqQueue.hvoid BroadFSearch(AdjMWGraph G,int v,int visited,void Visit(DataType item)DataType u,w;SeqCQueue queue;Visit(G.Vertices.listv);visitedv=1;QueueInitiate(,void BroadFirstSearch(AdjMWGraph G,void Visit(DataType item)int i;int*visited=(int*)malloc(sizeof(int)*
26、);for(i=0;i;i+)visitedi=0;for(i=0;i;i+)if(!visitedi)BroadFSearch(G,i,visited,Visit);free(visited);,四、非连通图的广度优先遍历,五、程序举例例9-2 以图9-8所示的带权有向图为例,编写测试上述图的深度优先和广度优先遍历函数的程序。#include#include#include typedef char DataType;#define MaxSize 100#define MaxVertices 10#define MaxEdges 100,#define MaxWeight 10000#de
27、fine MaxQueueSize 100#include AdjMGraph.h#include AdjMGraphCreate.h#include AdjMGraphTraverse.hvoid Visit(DataType item)printf(%c,item);,void main(void)AdjMWGraph g1;DataType a=A,B,C,D,E;RowColWeight rcw=0,1,10,0,4,20,1,3,30,2,1,40,3,2,50;int n=5,e=5;CreatGraph(,9.5 最小生成树,1.基本概念,一个有n个顶点的连通图的生成树是原图的极
28、小连通子图,它包含原图中的所有n个顶点,并且有保持图连通的最少的边。,(1)若在生成树中删除一条边就会使该生成树因变成非连通图而不再满足生成树的定义;(2)若在生成树中增加一条边就会使该生成树中因存在回路而不再满足生成树的定义;(3)一个连通图的生成树可能有许多;(4)有n个顶点的无向连通图,无论它的生成树的形状如何,一定有且只有n-1条边。,无向图和它的不同的生成树,如果无向连通图是一个带权图,那么它的所有生成树中必有一棵边的权值总和最小的生成树,我们称这棵生成树为最小代价生成树,简称最小生成树。,构造有n个顶点的无向连通带权图的最小生成树必须满足以下三条:(1)构造的最小生成树必须包括n个
29、顶点;(2)构造的最小生成树中有且只有n-1条边;(3)构造的最小生成树中不存在回路。,典型的构造方法有两种,一种称作普里姆(Prim)算法,另一种称作克鲁斯卡尔(Kruskal)算法。,2.普里姆算法,一、算法思想,假设G=(V,E)为一个带权图,V为带权图中顶点的集合,E为带权图中边的权值集合。设置两个新的集合U和T,U用于存放带权图G的最小生成树的顶点的集合,T用于存放带权图G的最小生成树的权值的集合。,普里姆算法思想是:(1)令集合U的初值为U=u0,集合T的初值为T=。(2)从所有顶点uU和顶点vV-U的带权边中选出具有最小权 值的边(u,v),将顶点v加入集合U中,将边(u,v)加
30、入集合T中。(3)判断U=V是否成立 不成立重复(2)成立时,最小生成树构造完毕。,假设构造最小生成树时从顶点u0开始,结论:最终集合U中存放着最小生成树顶点的集合,集合T中存放着最小生成树边的权值集合。,图9-10 普里姆算法构造最小生成树的过程,U=A,V-U=B,C,D,E,F,G,T=50,U=A,B,V-U=C,D,E,F,G,更新,T=50,40,U=A,B,E,V-U=C,D,F,G,更新,T=50,40,50,T=,40,50,50,U=A,B,E,D,V-U=C,F,G,更新,T=50,40,50,30,30,U=A,B,E,D,F,V-U=C,G,更新,T=50,40,50
31、,30,42,42,U=A,B,E,D,F,G,V-U=C,更新,T=50,40,50,30,42,45,45,U=A,B,E,D,F,G,C,V-U=,更新,U=A,V-U=B,C,D,E,F,G,T=,初始状态:,0,1,2,4,6,3,5,最小生成树,A,第1步:在U和V-U的顶点之间选择权值 最小的边对应的顶点。,0,60,50,B,50,U=A,B,V-U=C,D,E,F,G,更新,T=50,-1,0,1,2,4,6,3,5,最小生成树,A,第2步:在U和V-U的顶点之间选择权值 最小的边对应的顶点。,-1,60,50,B,50,U=A,B,V-U=C,D,E,F,G,第1步更新后,
32、T=50,-1,预处理:引入一个一维数组lowcost。,lowcost,lowcost初值为第1行权值,第1个改为-1,在第1步更新后对Lowcost进行更新,对更新的顶点对应的邻接矩阵中的行元素 从第1元素开始与lowcost中的元素进行比较,用小的替换lowcost中的值,65,40,E,40,二、普里姆函数设计,普里姆函数应有两个参数,一个参数是图G,另一个参数是通过函数得到的最小生成树的顶点数据和相应顶点的边的权值数据closeVertex。其数据类型定义为如下结构体:typeder struct VerT vertex;int weight;MinSpanTree;其中,verte
33、x域用来保存最小生成树每条边的弧头顶点数据,weight域用来保存最小生成树的相应边的权值。,图G,图G的最小生成树可以用MinSpanTree类型的结构数组和结构指针变量表示,普里姆算法可以用一个函数来实现,普里姆函数设计:void Prim(AdjMWGraph G,MinSpanTree closeVertex)VerT x;int n=,minCost;int*lowCost=(int*)malloc(sizeof(int)*n);int i,j,k;for(i=1;i n;i+)lowCosti=G.edge0i;/*从顶点0出发构造最小生成树*/ListGet(G.Vertices
34、,0,i+),/*寻找当前最小权值的边所对应的弧头顶点k*/minCost=MaxWeight;for(j=1;j 0)minCost=lowCostj;k=j;ListGet(G.Vertices,k,设计说明:(1)函数中定义一个临时数组lowCost,数组元素lowCostv中保存了集合U中顶点ui与集合V-U中顶点vj的所有边中当前具有最小权值的边(u,v)。(2)集合U的初值为U=序号为0的顶点。lowCost的初始值为邻接矩阵数组中第0行的值,这样初始时lowCost中就存放了从集合U中顶点0到集合V-U中各个顶点的权值。(3)每次从lowCost中寻找具有最小权值的边,根据low
35、Cost的定义,这样的边其弧头顶点必然为集合U中的顶点,其弧尾顶点必然为集合V-U中的顶点。当选到一条这样的边(u,v),就保存其顶点数据和权值数据到参数closeVertex中,并将lowCostv置为-1,表示顶点v加入了集合U中。,(4)当顶点v从集合V-U加入到集合U后,若存在一条边(u,v),u是集合U的顶点,v是集合V-U的顶点,且边(u,v)较原先lowCostv的代价更小,则用这样的权值修改原先lowCostv中的相应权值。(5)以图9-10(a)所示的无向连通带权图为例,调用普里姆函数时数组lowCost的动态变化过程如图示。,函数主要是一个两重循环,其中每一重循环的次数均为
36、顶点个数n,所以该算法的时间复杂度为O(n2)。,三、测试程序例9-3 以图9-10(a)所示的无向连通带权图为例设计测试上述Prim函数的程序。,3.克鲁斯卡尔算法,克鲁斯卡尔算法是一种按照带权图中边的权值的递增顺序构造最小生成树的方法。设无向连通带权图G=(V,E),其中V为顶点的集合,E为边的集合。克鲁斯卡尔算法思想是:设带权图G的最小生成树T由顶点集合和边的集合构成,其初值为T=(V,),即初始时最小生成树T只由带权图G中的顶点集合组成,各顶点之间没有一条边。这样,最小生成树T中的各个顶点各自构成一个连通分量。然后,按照边的权值递增的顺序考察带权图G中的边集合E中的各条边,若被考察的边
37、的两个顶点属于T的两个不同的连通分量,则将此边加入到最小生成树T,同时把两个连通分量连接为一个连通分量;若被考察的边的两个顶点属于T的同一个连通分量,则将此边舍去。如此下去,当T中的连通分量个数为1时,T中的该连通分量即为带权图G的一棵最小生成树,克鲁斯卡尔算法构造最小生成树的过程,9.6 最短路径,1.基本概念,路径长度:一条路径上所经过的边的数目,带权路径长度:路径上所经过边的权值之和,最短路径:(带权)路径长度(值)最小的那条路径,图9-13(a)有向带权图;(b)邻接矩阵,从顶点B到顶点D的路径:,路径(B,E,D)路径(B,A,D)路径(B,A,C,F,D)路径(B,A,C,F,E,
38、D),2.从一个顶点到其余各顶点的最短路径,一、狄克斯特拉算法,思想:初始假设:假设G=(V,E),集合S中存放已找到最短路径的顶点,集合T中存放当前还未找到最短路径的顶点。,按路径长度递增的顺序逐步产生最短路径。,算法依据,若从顶点S到顶点T有一条最短路径,则该路径上任何顶点到顶点S的距离都是最短的。,设A为源点,求A到其他各顶点(B、C、D、E、F)的最短路径。,求解过程,具体步骤:初始状态,集合S=源点=v0,T=V-S;然后从集合T中选择到源点v0路径长度最短的顶点u加入到集合S中,集合S中每加入一个新的顶点u都要修改源点v0到集合T中剩余顶点的当前最短路径长度值.修改原则:集合T中各
39、顶点的新的当前最短路径长度值,为原来的当前最短路径长度值与从源点过顶点u到达该顶点的路径长度中的较小者。此过程不断重复,直到集合T中的顶点全部加入到集合S中为止。,引入标记数组S,引入路径长度数组distance,引入记录最短路径前一个顶点数组path,一般的,假设u是新加入集合S的点,完成以下步骤:计算源点v0到T中各个顶点的最短距离;另一个方面计算源点v0到经顶点u到达T中各个顶点的距离,distance,d0,d1,d2,du,dn-1,标记S,s0,s1,s2,1,sn-1,计算公式为:du+d(u,y),其中y是T中的顶点,也就是标记为0的顶点,根据上述准则修改distance中的值
40、,如果distanceydu+d(u,y),则用du+d(u,y)替换distzncey,y,狄克斯特拉算法求从顶点A到其余各顶点最短路径的过程,二、狄克斯特拉函数设计,参数设计:函数共有4个参数,其中2个为输入参数,分别为带权图G和源点序号v0;2个为输出参数,分别为distance和path,distance用来存放得到的从源点v0到其余各顶点的最短距离数值,path用来存放得到的从源点v0到其余各顶点的最短路径上到达目标顶点的前一顶点下标。,狄克斯特拉函数设计:void Dijkstra(AdjMGraph G,int v0,int distance,int path)/*带权图G从下标
41、v0顶点到其它顶点的最短距离distance*/*和最短路径下标path*/int n=;int*s=(int*)malloc(sizeof(int)*n);int minDis,i,j,u;/*初始化*/for(i=0;i n;i+)distancei=G.edgev0i;si=0;if(i!=v0,/*在还未找到最短路径的顶点集中选有最短距离的顶点u*/for(i=1;i n;i+)minDis=MaxWeight;for(j=0;j=n;j+)if(sj=0,三、测试程序 例9-4:以图9-13的有向带权图为例设计测试上述Dijkstra函数的程序。,3.每对顶点之间的最短路径,带权有向
42、图,每对顶点之间的最短路径可通过调用狄克斯特拉算法实现。具体方法是:每次以不同的顶点作为源点,调用狄克斯特拉算法求出从该源点到其余顶点的最短路径。重复n次就可求出每对顶点之间的最短路径。由于狄克斯特拉算法的时间复杂度为O(n2),所以这种算法的时间复杂度为O(n3)。,弗洛伊德算法的思想是:设矩阵cost用来存放带权有向图G的权值,即矩阵元素costij中存放着下标为i的顶点到下标为j的顶点之间的权值,可以通过递推构造一个矩阵序列A0,A1,A2,AN来求每对顶点之间的最短路径。其中,Akij(0kn)表示从顶点vi到顶点vj的路径上所经过的顶点下标不大于k的最短路径长度。初始时有,A0ij=
43、costij。,一种情况是该路径不经过下标为k+1的顶点,此时该路径长度与从顶点vi到顶点vj的路径上所经过的顶点下标不大于k的最短路径长度相同;,另一种情况是该路径经过下标为k+1的顶点,此时该路径可分为两段,一段是从顶点vi到顶点vk+1的最短路径,另一段是从顶点vk+1到顶点vj的最短路径,此时的最短路径长度等于这两段最短路径长度之和。,当已经求出Ak,要递推求解Ak+1时,有:,这两种情况中的路径长度较小者,就是要求的从顶点vi到顶点vj的路径上所经过的顶点下标不大于k+1的最短路径长度。,用公式描述为:A0ij=costijAk+1ij=minAkij,Akik+1+Akk+1j(0
44、kn-1),9.7 拓扑排序,1偏序关系和全序关系 拓扑排序就是要由某个集合上的偏序关系得到该集合上的全序关系。若集合X上的关系R是自反的、反对称的和传递的,则称关系R是集合X上的偏序关系(或称半序关系)。设关系R为定义在集合X上的二元关系,若对于每一个xX,都有(x,x)R,则称R是自反的。设关系R为定义在集合X上的二元关系,如果对于任意的x,y,zX,若当(x,y)R且(y,z)R时,有(x,z)R,则称关系R是传递的。,设关系R为定义在集合X上的二元关系,若对于所有的x,yX,若当(x,y)R且(y,x)R时,有x=y,则称关系R是反对称的。例如,设X是实数集合,R为小于等于关系,即R=
45、(x,y)|xXyXxy,由于当xy且yx时有xy,因此,关系R是反对称关系。另外,相等关系也是反对称关系。设关系R是集合X上的偏序关系,若对所有的x,yX,有(x,y)R或(y,x)R,则称关系R是集合X上的全序关系。偏序关系的实质是在集合X的元素之间建立层次结构。这种层次结构是依赖于偏序关系的可比性建立起来的。但是,偏序关系不能保证集合X中的任意两个元素之间都能比较。而全序关系可以保证集合中的任意两个元素之间都可以比较。,2有向图在实际问题中的应用 一个有向图可以表示一个施工流程图,或产品生产流程图,或数据流图等。设图中每一条有向边表示两个子工程之间的先后次序关系。若以有向图中的顶点来表示
46、活动,以有向边来表示活动之间的先后次序关系,则这样的有向图称为顶点表示活动的网(Activity On Vertex Network),简称AOV网。AOV网表示的有向图要解决的一个问题,就是如何得到一个完成整个工程项目的各子工程的序列。这就是有向图的拓扑排序问题。,3拓扑排序在有向图中的应用 如果把有向图中的所有顶点看作集合中的元素,把有向图中的有向边看作集合中的关系(通常情况下是先于关系),则可以证明,如果有向图中不存在回路,则对应的集合上的关系满足偏序关系。因此,如何得到AOV网表示的施工流程图的一个完成整个工程项目的各子工程的序列问题,就是一个AOV网表示的有向图的拓扑排序问题。对一个
47、有向图进行拓扑排序,就是将有向图中的所有顶点排成一个线性序列,使得对有向图中任意一对顶点u和v,若是有向图中的一条有向边,则顶点u在该线性序列中出现在顶点v之前。这样的线性序列称为满足拓扑次序的序列,简称为拓扑序列。对有向图建立拓扑序列的过程称为对有向图的拓扑排序。,对于AOV网的拓扑排序就是将AOV网中的所有顶点排成一个线性序列。拓扑排序实际上就是要由某个集合上的一个偏序关系得到该集合上的一个全序关系。,例如,下图所示的两个有向图,由于左图中顶点B无法和顶点C比较,所以左图表示的是偏序关系。如果在左图中人为添加一条顶点B到顶点C的有向边,即右图,则右图表示的就是全序关系。如果我们对一个有向图
48、进行拓扑排序,得到了该有向图中所有顶点的一个线性序列,因线性序列中所有顶点均可以比较,也就相当于通过人为添加一些有向边,把有向图对应的偏序关系变成了全序关系。,4有向图的拓扑排序算法 有向图的拓扑排序算法:(1)在有向图中选择一个没有前驱的顶点,并把它输出;(2)从有向图中删去该顶点以及与它相关的有向边。重复上述两个步骤,直到所有顶点都输出,或者剩余的顶点中找不到没有前驱的顶点。在前一种情况下,顶点的输出序列就是一个拓扑序列;后一种情况则说明有向图中存在回路,如果有向图中存在回路,则该有向图一定无法得到一个拓扑序列。,上述算法仅能得到有向图的一个拓扑序列。改进上述算法,可以得到有向图的所有拓扑
49、序列。如果一个有向图存在一个拓扑序列,通常表示该有向图对应的某个施工流程图的一种施工方案切实可行;而如果一个有向图不存在一个拓扑序列,则说明该有向图对应的某个施工流程图存在设计问题,不存在切实可行的任何一种施工方案。,例:对于如下图所示的AOV网,写出利用拓扑排序算法得到的一个拓扑序列。解:根据拓扑排序算法,得到的一个拓扑序列为0,1,7,2,4,8,5,9,3,6。,9.8关键路径,1工程管理中的问题 在工程规划中,经常需要考虑这样的问题,完成整个工程最短需要多长时间,工程中的哪些工序是一些重要的工序,缩短这些重要工序的时间可以缩短整个工程的工期。在生产管理中,也存在这样的问题,一件产品有多
50、道生产工序,减少哪道工序所用的时间可以缩短产品的生产周期。诸如此类的问题,可以使用有向图进行描述和分析。下面我们首先给出描述这类问题的有关概念,然后讨论解决的方法。,2AOE网对工程管理问题的表示 在有向图中,如果顶点表示事件,有向边表示活动,有向边上的权值表示活动持续的时间,这样的有向图称为边表示活动的网(Activity On Edge),简称AOE网。对于AOE网,需要研究的问题是:(1)完成整个工程需要多少时间?(2)哪些活动是影响工程进度的关键?,a9=6,a6=4,a2=7,a1=5,a3=3,a4=6,a5=3,a7=5,a8=4,a10=5,a11=8,a14=9,a12=2,