1、图的基本概念 图的存储结构 图的遍历 图的连通性问题 最小生成树 最短路径 活动网络,第七章 图,图的基本概念,图定义 图是由顶点集合(vertex)及顶点间的关系集合组成的一种数据结构:Graph( V, E ) 其中 V = x | x 某个数据对象 是顶点的有穷非空集合;E1 = (x, y) | x, y V 或 E2 = | x, y V & Path (x, y)其中, E1是顶点之间关系的有穷集合,也叫做边(edge)集合,此时的图称为无向图。 E2 表示从 x 到 y 的一条弧,且称x为弧尾,y为弧头,这样的图称为有向图。,有向图与无向图 在有向图中,顶点对是有序的。在无向图中
2、,顶点对(x, y)是无序的。 完全图 若有 n 个顶点的无向图有 n(n-1)/2 条边, 则此图为无向完全图。有 n 个顶点的有向图有n(n-1) 条边, 则此图为有向完全图。,0,0,0,0,1,1,1,1,2,2,2,2,6,5,4,3,3,邻接顶点 如果 (u, v) 是 E(G) 中的一条边,则称 u 与 v 互为邻接顶点。 子图 设有两个图 G(V, E) 和 G(V, E)。若 V V 且 EE, 则称 图G 是 图G 的子图。权 某些图的边具有与它相关的数, 称之为权。这种带权图叫做网络。,顶点的度 一个顶点v的度是与它相关联的边的条数。记作D(v)。在有向图中, 顶点的度等
3、于该顶点的入度与出度之和。 顶点 v 的入度是以 v 为终点的有向边的条数, 记作 ID(v); 顶点 v 的出度是以 v 为始点的有向边的条数, 记作 OD(v)。 路径 在图 G(V, E) 中, 若从顶点 vi 出发, 沿一些边经过一些顶点 vp1, vp2, , vpm,到达顶点vj。则称顶点序列 (vi vp1 vp2 . vpm vj) 为从顶点vi 到顶点 vj 的路径。它经过的边(vi, vp1)、(vp1, vp2)、.、(vpm, vj) 应是属于E的边。,顶点的出度: 以顶点v 为弧尾的弧的数目; 顶点的入度: 以顶点v为弧头的弧的数目。,有向图,顶点的度D=出度(OD)
4、+入度(ID) D(B) =OD(B)+2ID(B) = 3,例如:,路径长度 非带权图的路径长度是指此路径上边的条数。带权图的路径长度是指路径上各边的权之和。 简单路径 若路径上各顶点 v1,v2,.,vm 均不 互相重复, 则称这样的路径为简单路径。 简单回路 若路径上第一个顶点 v1 与最后一个顶点vm 重合, 则称这样的路径为回路或环。,0,1,2,3,0,1,2,3,0,1,2,3,如:从A到F长度为 3 的路径A,B,C,F,连通图与连通分量 在无向图中, 若从顶点v1到顶点v2有路径, 则称顶点v1与v2是连通的。如果图中任意一对顶点都是连通的, 则称此图是连通图。非连通图的极大
5、连通子图叫做连通分量。强连通图与强连通分量 在有向图中, 若对于每一对顶点vi和vj, 都存在一条从vi到vj和从vj到vi的路径, 则称此图是强连通图。非强连通图的极大强连通子图叫做强连通分量。,无向图,若图中任意两个顶点之间都有路径相通,则称此图为连通图;,若无向图为非连通图,则图中各个极大连通子图称作此图的连通分量。,有向图,若任意两个顶点之间都存在一条有向路径,则称此有向图为强连通图。 否则,其各个强连通子图称作它的强连通分量。,A,B,E,C,F,生成树:假设一个连通图有 n 个顶点和 e 条边,其中 n-1 条边和 n 个顶点构成一个极小连通子图,称该极小连通子图为此连通图的生成树
6、。在极小连通子图中增加一条边,则一定有环。 在极小连通子图中去掉一条边,则成为非连通图。,B,A,C,D,F,E,有n个顶点,n-1条边的图必定是生成树吗?,对非连通图,则称由各个连通分量的生成树的集合为此非连通图的生成森林。,图的存储结构,在图的邻接矩阵表示中,有一个记录各个顶点信息的顶点表,还有一个表示各个顶点之间关系的邻接矩阵。 设图 A = (V, E)是一个有 n 个顶点的图, 图的邻接矩阵是一个二维数组 A.edgenn,定义:,邻接矩阵 (Adjacency Matrix),无向图的邻接矩阵是对称的; 有向图的邻接矩阵可能是不对称的。,0,1,2,3,0,1,2,在有向图中, 统
7、计第 i 行 1 的个数可得顶点 i 的出度,统计第 j 列 1 的个数可得顶点 j 的入度。 在无向图中, 统计第 i 行 (列) 1 的个数可得顶点i 的度。,网络的邻接矩阵,#define e 8 /边条数 #define n 6 /顶点个数 typedef char Vextype; /顶点数据类型 typedef float adjtype; /边上权值类型 typedef struct Vextype vexsn; /顶点表adjtype arcsnn;/邻接矩阵, 可视为边之间的关系 graph;,用邻接矩阵表示的结构定义,下面给出建立一个无向网络的算法,CREATGRAPH(
8、graph *ga ) int i, j, k ; float w ;for ( i=0 ; ivexsi=getchar( ); for ( i=0 ; iarcsij=0 ;for ( k=0 ; karcsij=w; ga-ji=w; ,邻接表 (Adjacency List),邻接表:是图的一种链式存储结构。 弧的结点结构adjvex; / 该弧所指向的顶点的位置nextarc;/ 指向下一条弧指针info; / 该弧相关信息的指针,顶点的结点结构,data; / 顶点信息 firstarc; /指向第一条依附该顶点的弧,无向图的邻接表,同一个顶点发出的边链接在同一个边链表中,每一个链
9、结点代表一条边(边结点), 结点中有另一顶点的下标 dest 和指针 link。,有向图的邻接表和逆邻接表,A,B,C,data adj,A B C,0 1 2,dest link,dest link,邻接表 (出边表),data adj,A B C,0 1 2,dest link,逆邻接表 (入边表),1,0,2,0,1,1,网络 (带权图) 的邻接表,B,A,C,D,6,9,5,2,8,data adj,A B C D,0 1 2 3,dest cost link,1 5,3 6,2 8,3 2,1 9,(出边表),(顶点表),图的邻接表存储表示#define MAX_VERTEX_NUM
10、 20 typedef struct Node int adjvex; / 该弧所指向的顶点的位置struct Node *next;/ 指向下一条弧指针InfoType *info; / 该弧相关信息的指针 edgeNode; typedef struct Vextype vertex; / 顶点信息edgeNode *link; / 指向第一条依附该顶点的弧 VexNode; VexNodeMAX_VERTEX_NUM;,邻接表的构造算法 void CREATADJLIST(vexnode ga ) int i, j, k ; edgenode *s ;for ( i = 0; i adj
11、vex= j; s-next = gai.link;,gai-link = s; s=malloc(sizeof(edgenode);s-adjvex = i; s-next= gaj.link;gaj-link = s; ,图的遍历,从图中某一顶点出发访遍图中所有的顶点,且使每个顶点仅被访问一次,这一过程就叫做图的遍历 (Traversing Graph)。 图中可能存在回路,且图的任一顶点都可能与其它顶点相通,在访问完某个顶点之后可能会沿着某些边又回到了曾经访问过的顶点。 为了避免重复访问,可设置一个标志顶点是否被访问过的辅助数组 visited 。,辅助数组 visited 的初始状态为
12、 0, 在图的遍历过程中, 一旦某一个顶点 i 被访问, 就立即让 visited i 为 1, 防止它被多次访问。 两种图的遍历方法: 深度优先搜索 DFS (Depth First Search) 广度优先搜索 BFS (Breadth First Search),深度优先搜索DFS ( Depth First Search ),深度优先搜索过程,6,7,前进 回退,深度优先生成树,DFS 在访问图中某一起始顶点 v 后, 由 v 出发, 访问它的任一邻接顶点 w1; 再从 w1 出发,访问与 w1邻 接但还没有访问过的顶点 w2; 然后再从 w2 出发, 进行类似的访问, 如此进行下去,
13、 直至到达所有的邻接顶点都被访问过的顶点 u 为止。接着, 退回一步, 退到前一次刚访问过的顶点, 看是否还有其它没有被访问的邻接顶点。如果有, 则访问此顶点, 之后再从此顶点出发, 进行与前述类似的访问; 如果没有, 就再退回一步进行搜索。重复上述过程, 直到连通图中所有顶点都被访问过为止。,图的深度优先搜索算法 int visitedn; graph g; void DFS ( int i ) int j ; for ( j = 0; j n; j+ ) visited j = 0; /访问数组 visited 初始化printf(“node:%cn”,g.vexsi); visitedi
14、=1;for ( j= 0; j n; j+ ) if (g.arcsij=1 /从顶点 j 出发开始搜索,void DFSL ( int i ) int j ; edgenode *p ;printf(“node:%cn”,g1i.vertex); /访问顶点 ivisitedi = 1; /顶点 v 作访问标记p=g1i.link; /取 i 的第一个邻接顶点 pwhile ( p != NULL ) /若邻接顶点存在 if ( !visitedp-adjvex ) DFSL (p-adjvex);/若顶点 p未访问过, 递归访问顶点 pp=p-next;/取顶点 i排在 p后的下一个邻接
15、顶点 ,广度优先搜索BFS ( Breadth First Search ),广度优先搜索过程,A,C,D,E,G,B,F,I,H,A,C,D,E,G,B,F,H,1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9,广度优先生成树,I,BFS在访问了起始顶点 v 之后, 由 v 出发, 依次访问 v 的各个未被访问过的邻接顶点 w1, w2, , wt, 然后再顺序访问 w1, w2, , wt 的所有还未被访问过的邻接顶点。再从这些访问过的顶点出发,再访问它们的所有还未被访问过的邻接顶点, 如此做下去,直到图中所有顶点都被访问到为止。 广度优先搜索是一种分层的搜索过程,
16、每向前走一步可能访问一批顶点, 不像深度优先搜索那样有往回退的情况。因此, 广度优先搜索不是一个递归的过程。,为了实现逐层访问, 算法中使用了一个队列,以记忆正在访问的这一层和下一层的顶点,以便于向下一层访问。 为避免重复访问, 需要一个辅助数组 visited ,给被访问过的顶点加标记。,图的广度优先搜索算法 void DFS( int k ) int i ,j ; SETNULL(Q);printf(“%cn”,g.vexsk);visitedk=1; ENQUEUE(Q,k); while ( ! EMPTY(Q) )i=DEQUEUE(Q);,for(j=0 ;j n ; j+ )if
17、 ( g.arcsij=1),while ( !EMPTY(Q) i=DEQUEUE(Q); p=g1i.link;while( p!=NULL) if(!visitedp-adjvex)printf(“%cn”,g1p-adjvex.vertex);visitedp-sdjvex=1;ENQUEUE(Q,p-adjvex); p=p-next;,连通分量 (Connected component) 当无向图为非连通图时, 从图中某一顶点出发, 利用深度优先搜索算法或广度优先搜索算法不可能遍历到图中的所有顶点, 只能访问到该顶点所在的最大连通子图(连通分量)的所有顶点。 若从无向图的每一个连通
18、分量中的一个顶点出发进行遍历, 可求得无向图的所有连通分量。,图的连通性问题,求连通分量的算法需要对图的每一个顶点进行检测:若已被访问过,则该顶点一定是落在图中已求得的连通分量上;若还未被访问,则从该顶点出发遍历图,可求得图的另一个连通分量。 对于非连通的无向图,所有连通分量的生成树组成了非连通图的生成森林。,A,C,D,E,I,B,F,O,G,H,J,N,M,L,K,非连通无向图的三个连通分量,A,H,K,C,D,E,I,B,F,O,G,J,N,M,L,非连通图的连通分量的极小连通子图,非连通图的遍历算法,TRAVER( ) int i;for (i=0; in; i+ )visitedi=
19、FALSE;for (i=0; in; i+ ) if (!visitedi )DFS( i );printf ( “comp end n” );,重连通分量 (Biconnected Component),在无向连通图G中, 当且仅当删去G中的顶点v及所有依附于v的所有边后, 可将图分割成两个或两个以上的连通分量,则称顶点v为关节点。 没有关节点的连通图叫做重连通图。 在重连通图上, 任何一对顶点之间至少存在有两条路径, 在删去某个顶点及与该顶点相关联的边时, 也不破坏图的连通性。 一个连通图G如果不是重连通图,那么它可以包括几个重连通分量。,在一个无向连通图G中, 重连通分量可以利用深度优
20、先生成树找到。 在图中各顶点旁标明的深度优先数, 给出进行深度优先搜索时各顶点访问的次序。 深度优先生成树的根是关节点的充要条件是它至少有两个子女。,A,B,C,D,E,F,G,H,I,J,A,B,C,D,E,F,G,H,J,A,B,C,D,E,F,G,H,J,I,I,1,2,3,4,5,6,7,8,9,10,根有两 个子女,关节点,关节点,关节点,连通图和它的连通分量,最小生成树 ( minimum cost spanning tree ),使用不同的遍历图的方法,可以得到不同的生成树;从不同的顶点出发,也可能得到不同的生成树。 按照生成树的定义,n 个顶点的连通网络的生成树有 n 个顶点、
21、n-1 条边。 构造最小生成树的准则 必须使用且仅使用该网络中的n-1 条边来联结网络中的 n 个顶点; 不能使用产生回路的边; 各边上的权值的总和达到最小。,普里姆(Prim)算法,普里姆算法的基本思想:从连通网络 N = V, E 中的某一顶点 u0 出发, 选择与它关联的具有最小权值的边 ( u0, v ), 将其顶点加入到生成树顶点集合U中。以后每一步从一个顶点在 U 中,而另一个顶点不在 U 中的各条边中选择权值最小的边(u, v), 把它的顶点加入到集合 U 中。如此继续下去, 直到网络中的所有顶点都加入到生成树顶点集合 U 中为止。 采用邻接矩阵作为图的存储表示。,25,25,1
22、0,5,0,4,6,1,3,2,28,10,25,14,24,22,16,18,5,0,4,6,1,3,2,5,0,4,6,1,3,2,10,原图 (a) (b),5,0,4,6,1,3,2,10,(c) (d) (e) (f),5,0,4,6,1,3,2,10,22,12,5,0,4,6,1,2,10,25,14,22,16,12,3,25,22,12,在构造过程中,还设置了两个辅助数组:lowcost 存放生成树顶点集合内顶点到生成树外各顶点的各边上的当前最小权值;nearvex 记录生成树顶点集合外各顶点距离集合内哪个顶点最近(即权值最小)。 例子,5,0,4,6,1,3,2,28,10
23、,25,14,24,22,16,18,12,若选择从顶点0出发,即u0 = 0,则两个辅助数组的初始状态为:然后反复做以下工作:在 lowcost 中选择 nearvexi -1 & lowcosti最小的边, 用 v 标记它。则选中的权值最小的边为(nearvexv, v), 相应的权值为 lowcostv。,0 28 10 ,-1 0 0 0 0 0 0,lowcost,nearvex,0 1 2 3 4 5 6,将 nearvexv 改为-1, 表示它已加入生成树顶点集合。 将边 (nearvexv, v, lowcostv ) 加入生成树的边集合。 取 lowcosti = min l
24、owcosti, Edgevi ,即用生成树顶点集合外各顶点 i 到刚加入该集合的新顶点 v 的距离 Edgevi 与原来它们到生成树顶点集合中顶点的最短距离lowcosti 做比较, 取距离近的作为这些集合外顶点到生成树顶点集合内顶点的最短距离。,如果生成树顶点集合外顶点 i 到刚加入该集合的新顶点 v 的距离比原来它到生成树顶点集合中顶点的最短距离还要近,则修改nearvexi : nearvexi = v。表示生成树外顶点i到生成树内顶点v当前距离最近。,0 28 10 ,-1 0 0 0 0 0 0,lowcost,nearvex,0 1 2 3 4 5 6,选 v=5,选边 (0,5
25、),顶点v=5加入生成树顶点集合:,0 28 25 10 ,-1 0 0 0 5 -1 0,lowcost,nearvex,0 1 2 3 4 5 6,选 v=4,选边 (5,4),5,0,4,6,1,3,2,28,10,25,14,24,22,16,18,原图 边 (0,5,10) 加入生成树,12,0,4,6,1,3,2,10,25,5,0 1 2 3 4 5 6,顶点v=4加入生成树顶点集合:,0 28 22 25 10 24,-1 0 0 4 -1 -1 4,lowcost,nearvex,选 v=3,选边 (4,3),5,0,4,6,1,3,2,28,10,25,14,24,22,1
26、6,18,原图 边 (5,4,25) 加入生成树,12,5,0,4,6,1,3,2,10,25,22,顶点v=3加入生成树顶点集合:,0 28 12 22 25 10 18,-1 0 3 -1 -1 -1 3,lowcost,nearvex,0 1 2 3 4 5 6,选 v=2,选边 (3,2),5,0,4,6,1,3,2,28,10,25,14,24,22,16,18,原图 边 (4,3,22) 加入生成树,12,5,0,4,6,1,3,2,10,25,22,12,lowcost,nearvex,0 1 2 3 4 5 6,顶点v=2加入生成树顶点集合:,0 16 12 22 25 10
27、18,-1 2 -1 -1 -1 -1 3,选 v=1,选边 (2,1),5,0,4,6,1,3,2,28,10,25,14,24,22,16,18,原图 边 (3,2,12) 加入生成树,12,5,0,4,1,3,2,10,25,22,16,12,顶点v=1加入生成树顶点集合:,0 16 12 22 25 10 14,-1 -1 -1 -1 -1 -1 1,lowcost,nearvex,0 1 2 3 4 5 6,选 v=6,选边 (1,6),5,0,4,6,1,3,2,28,10,25,14,24,22,16,18,原图 边 (2,1,16) 加入生成树,12,5,0,4,6,1,3,2
28、,10,25,14,22,16,12,lowcost,nearvex,0 1 2 3 4 5 6,顶点v=6加入生成树顶点集合:,0 16 12 22 25 10 14,-1 -1 -1 -1 -1 -1 -1,5,0,4,6,1,3,2,28,10,25,14,24,22,16,18,原图 边 (1,6,14) 加入生成树,12,5,0,4,6,1,3,2,10,25,14,22,16,12,最后生成树中边集合里存入得各条边为:(0, 5, 10), (5, 4, 25), (4, 3, 22),(3, 2, 12), (2, 1, 16), (1, 6, 14),利用普里姆算法建立最小生成
29、树void Prim ( Graph G, MST /及最短带权路径,nearvexu = -1; /加到生成树顶点集合 int k = 0; /存放最小生成树结点的变量for ( i = 0; i G.n; i+ )if ( i != u ) /循环n-1次, 加入n-1条边 EdgeData min = MaxValue; int v = 0; for ( int j = 0; j NumVertices; j+ ) if ( nearvexj != -1 /求生成树外顶点到生成树内顶点具有最/小权值的边, v是当前具最小权值的边,if ( v ) /v=0表示再也找不到要求顶点Tk.ta
30、il = nearvexv; /选边加入生成树Tk.head = v;Tk+.cost = lowcostv; nearvexv = -1; /该边加入生成树标记for ( j = 0; j G.n; j+ )if ( nearvexj != -1 , /循环n-1次, 加入n-1条边 分析以上算法, 设连通网络有 n 个顶点, 则该算法的时间复杂度为 O(n2), 它适用于边稠密的网络。 注意:当各边有相同权值时,由于选择的随意性,产生的生成树可能不唯一。当各边的权值不相同时,产生的生成树是唯一的。,活动网络 (Activity Network),计划、施工过程、生产流程、程序流程等都是“工
31、程”。除了很小的工程外,一般都把工程分为若干个叫做“活动”的子工程。完成了这些活动,这个工程就可以完成了。 例如,计算机专业学生的学习就是一个工程,每一门课程的学习就是整个工程的一些活动。其中有些课程要求先修课程,有些则不要求。这样在有的课程之间有领先关系,有的课程可以并行地学习。,用顶点表示活动的网络 (AOV网络),C1 高等数学C2 程序设计基础C3 离散数学 C1, C2 C4 数据结构 C3, C2C5 高级语言程序设计 C2C6 编译方法 C5, C4C7 操作系统 C4, C9C8 普通物理 C1C9 计算机原理 C8,课程代号 课程名称 先修课程,学生课程学习工程图,C8,C3
32、,C5,C4,C9,C6,C7,C1,C2,可以用有向图表示一个工程。在这种有向图中,用顶点表示活动,用有向边表示活动Vi 必须先于活动Vj 进行。这种有向图叫做顶点表示活动的AOV网络 (Activity On Vertices)。 在AOV网络中不能出现有向回路, 即有向环。如果出现了有向环,则意味着某项活动应以自己作为先决条件。 因此,对给定的AOV网络,必须先判断它是否存在有向环。,检测有向环的一种方法是对AOV网络构造它的拓扑有序序列。即将各个顶点 (代表各个活动)排列成一个线性有序的序列,使得AOV网络中所有应存在的前驱和后继关系都能得到满足。 这种构造AOV网络全部顶点的拓扑有序
33、序列的运算就叫做拓扑排序。 如果通过拓扑排序能将AOV网络的所有顶点都排入一个拓扑有序的序列中, 则该网络中必定不会出现有向环。,如果AOV网络中存在有向环,此AOV网络所代表的工程是不可行的。 例如, 对学生选课工程图进行拓扑排序, 得到的拓扑有序序列为C1 , C2 , C3 , C4 , C5 , C6 , C8 , C9 , C7 或 C1 , C8 , C9 , C2 , C5 , C3 , C4 , C7 , C6,C8,C3,C5,C4,C9,C6,C7,C1,C2,拓扑排序的方法, 输入AOV网络。令 n 为顶点个数。 在AOV网络中选一个没有直接前驱的顶点, 并输出之; 从图
34、中删去该顶点, 同时删去所有它发出的有向边; 重复以上 、步, 直到全部顶点均已输出,拓扑有序序列形成,拓扑排序完成;或图中还有未输出的顶点, 但已跳出处理循环。说明图中还剩下一些顶点, 它们都有直接前驱。这时网络中必存在有向环。,C0,C1,C2,C3,C4,C5,拓扑排序的过程,(a) 有向无环图,C2,C5,C1,C0,C3,(b) 输出顶点C4,C1,C2,C5,C3,(c) 输出顶点C0,C4,C0,C2,C5,C1,C3,(d) 输出顶点C3,C1,C2,C5,(e) 输出顶点C2,C5,C1,(f) 输出顶点C1,C5,(g) 输出顶点C5,最后得到的拓扑有序序列为 C4 , C
35、0 , C3 , C2 , C1 , C5 。它满足图中给出的所有前驱和后继关系,对于本来没有这种关系的顶点,如C4和C2,也排出了先后次序关系。,(h) 拓扑排序完成,AOV网络及其邻接表表示,C0,C1,C2,C3,C4,C5,C0C1C2C3 0C4C5 0,0 1 2 3 4 5,count data adj,1 3 0 1 0 3,1,dest link,3 0,5,1,5 0,0,1,5 0,在邻接表中增设一个数组count ,记录各顶点入度。入度为零的顶点即无前驱顶点。 在输入数据前, 顶点表VexList 和入度数组count 全部初始化。在输入数据时, 每输入一条边, 就需要
36、建立一个边结点, 并将它链入相应边链表中, 统计入度信息:EdgeNode * p = new EdgeNode; p-dest = k; /建立边结点p-link = G.VexListj.firstAdj;VexListj.firstAdj = p; /链入顶点 j 的边链表的前端countk+; /顶点 k 入度加一,在算法中, 使用一个存放入度为零的顶点的链式栈, 供选择和输出无前驱的顶点。 拓扑排序算法可描述如下: 建立入度为零的顶点栈; 当入度为零的顶点栈不空时, 重复执行 从顶点栈中退出一个顶点, 并输出之;从AOV网络中删去这个顶点和它发出的边, 边的终顶点入度减一;如果边的终
37、顶点入度减至0, 则该顶点进入度为零的顶点栈;如果输出顶点个数少于AOV网络的顶点个数, 则报告网络中存在有向环。,拓扑排序的算法void TopologicalSort (AdjGraph G) Stack S; StackEmpty(S); int j; /入度为零的顶点栈初始化for ( int i = 0; i n; i+ ) /入度为零顶点if ( counti = 0 ) Push(S, i); /进栈for ( i = 0; i n; i+ ) /期望输出 n 个顶点if ( StackEmpty(S) ) /中途栈空,转出cout “网络中有回路!“ endl;return;,
38、else /继续拓扑排序Pop( S, j ); /退栈cout dest; /另一顶点 if ( -countk = 0 ) /顶点入度减一Push( S, k ); /顶点的入度减至零, 进栈p = p-link; ,用边表示活动的网络(AOE网络),如果在无有向环的带权有向图中, 用有向边表示一个工程中的活动 (Activity), 用边上权值表示活动持续时间 (Duration), 用顶点表示事件 (Event), 则这样的有向图叫做用边表示活动的网络, 简称 AOE ( Activity On Edges ) 网络。 AOE网络在某些工程估算方面非常有用。例如,可以使人们了解: 完成
39、整个工程至少需要多少时间(假设网络中没有环)?,为缩短完成工程所需的时间, 应当加快哪些活动? 从源点到各个顶点, 以至从源点到汇点的有向路径可能不止一条。 这些路径的长度也可能不同。 完成不同路径的活动所需的时间虽然不同, 但只有各条路径上所有活动都完成了, 整个工程才算完成。 因此, 完成整个工程所需的时间取决于从源点到汇点的最长路径长度, 即在这条路径上所有活动的持续时间之和。这条路径长度最长的路径就叫做关键路径(Critical Path)。,a9=6,1,3,2,4,a1=8,a2=12,5,6,7,8,a10=12,a8=18,a5=28,a6=8,a7=6,a3=14,a4=10
40、,要找出关键路径,必须找出关键活动, 即不按期完成就会影响整个工程完成的活动。 关键路径上的所有活动都是关键活动。因此, 只要找到了关键活动, 就可以找到关键路径。例如, 下图就是一个AOE网。,定义几个与计算关键活动有关的量:, 事件Vi 的最早可能开始时间Ve(i) 是从源点V0 到顶点Vi 的最长路径长度。 事件Vi 的最迟允许开始时间Vli是在保证汇点Vn-1 在Ven-1 时刻完成的前提下,事件Vi 的允许的最迟开始时间。 活动ak 的最早可能开始时间 ek设活动ak 在边上, 则ek是从源点V0到顶点Vi 的最长路径长度。因此, ek = Vei。 活动ak 的最迟允许开始时间 l
41、k,lk是在不会引起时间延误的前提下, 该活动允许的最迟开始时间。lk = Vlj - dur()。其中, dur()是完成 ak 所需的时间。 时间余量 lk - ek表示活动 ak 的最早可能开始时间和最迟允许开始时间的时间余量。lk = ek 表示活动 ak 是没有时间余量的关键活动。 为找出关键活动, 需要求各个活动的 ek 与 lk,以判别是否 lk = ek。,为求得 ek与 lk, 需要先求得从源点 V0 到各个顶点 Vi 的 Vei 和 Vli。 求Vei的递推公式从 Ve0 = 0 开始,向前递推 S2, i = 1, 2, , n-1 S2 是所有指向Vi 的有向边的集合。
42、从Vln-1 = Ven-1开始,反向递推 S1, i = n-2, n-3, , 0 S1是所有源自Vi的有向边的集合。,这两个递推公式的计算必须分别在拓扑有序及逆拓扑有序的前提下进行。 设活动ak (k=1,2,e)在带权有向边 上, 其持续时间用dur () 表示, 则有ek = Vei;lk = Vlj - dur();k = 1, 2, , e。这样就得到计算关键路径的算法。 为了简化算法, 假定在求关键路径之前已经对各顶点实现了拓扑排序, 并按拓扑有序的顺序对各顶点重新进行了编号。,1,3,2,4,a1=8,a2=12,5,6,7,8,a10=12,a9=6,a8=18,a5=28
43、,a6=8,a7=6,a3=14,a4=10,Ve Vl,1 2 3 4 5 6 7 8,0 8 12 22 28 40 46 580 8 12 22 28 40 46 58,e l,0 0 8 12 12 22 22 28 40 46 0 0 8 12 12 32 22 28 40 46,1 2 3 4 5 6 7 8 9 10,事件 Vei VliV0 0 0V1 6 6V2 4 6V3 5 8V4 7 7V5 7 10V6 16 16V7 14 14V8 18 18,边 活动 a1 a2 a3 a4 a5 a6 a7 a8 a9 a10 a11e 0 0 0 6 4 5 7 7 7 16
44、 14l 0 2 3 6 6 8 7 7 10 16 14 l - e 0 2 3 0 2 3 0 0 3 0 0 关键 是 是 是 是 是 是,利用关键路径法求AOE网的各关键活动 void graph:CriticalPath ( ) /在此算法中需要对邻接表中单链表的结点加以 /修改, 在各结点中增加一个int域 cost, 记录该结 /点所表示的边上的权值。int i, j; int p, k; int e, l;Ve = new intn; Vl = new intn;for ( i = 0; i *p = NodeTablei.adj;/该顶点边链表链头指针pwhile ( p != NULL ) /找所有后继邻接顶点 k = pdest;/i的后继邻接顶点k if ( Vei + pcost Vek ) Vek = Vei + pcost; p = plink;/找下一个后继,