Transcript Chapter 7
数据结构
刘家芬 Sept 2012
第七章 图
• 线性结构:是研究数据元素之间的一对一关系。在这
种结构中,除第一个和最后一个元素外,任何一个元
素都有唯一的一个直接前驱和直接后继。
• 树结构:是研究数据元素之间的一对多的关系。在这
种结构中,每个元素对下可以有0个或多个元素相联
系,对上只有唯一的一个元素相关,数据元素之间有
明显的层次关系。
• 图结构:是研究数据元素之间的多对多的关系。在这
种结构中,任意两个元素之间可能存在关系。即结点
之间的关系可以是任意的,图中任意元素之间都可能
相关。
7.1 图的定义和术语
• 图(Graph)——图G是由两个集合V(G)和E(G)组成的,
V=Vertex
记为G=(V,E)
– V(G)是顶点的非空有限集
– E(G)是边的有限集合,边是顶点对
E=Edge
• 有向图——有向图G是由两个集合V(G)和E(G)组成的
– V(G)是顶点的非空有限集
– E(G)是有向边(也称弧)的有限集合
– 弧是顶点的有序对,记为<v,w>,v,w是顶点,v为弧尾,w
为弧头
• 无向图——无向图G是由两个集合V(G)和E(G)组成的
– V(G)是顶点的非空有限集
– E(G)是边的有限集合,边是顶点的无序对,记为(v,w)或
(w,v),并且(v,w)=(w,v)
图的示例
2
1
4
1
5
3
6
3
G1
5
2
7
4
6
G2
• 有向图G1 =(V,E)中:
V(G1)={1,2,3,4,5,6}
E(G1)={<1,2>, <2,1>, <2,3>, <2,4>, <3,5>, <5,6>, <6,3>}
• 无向图G2=(V,E)中:
V(G2)={1,2,3,4,5,6,7}
E(G2)={(1,2),(1,3),(2,3),(2,4),(2,5),(5,6),(5,7)}
4
完全图
• 有向完全图——有n(n-1)条弧的n个顶点的有向图
• 无向完全图——有n(n-1)/2条边的n个顶点的无向图
• 证明:有向完全图有n(n-1)条边。
证明:若是有向完全图,则顶点1必与所有其他顶点各有2
条连线,即有2(n-1)条边, 顶点2有2(n-2)条边,…,顶点
n-1有2条边,顶点n有0条边。总边数=2( n-1+ n-2+…+1
+0)=2(n-1+0)n/2= n(n-1)
• 证明:无向完全图有n(n-1)/2 条边。
证明:若是无向完全图,则顶点1必与所有其他顶点各有1
条连线,即有n-1条边,顶点2有n-2条边,…,顶点n-1有1
条边,顶点n有0条边。总边数= n-1+ n-2+…+1+0=(n1+0)n/2= n(n-1)/2
图的定义和术语
• 稀疏图——若边或弧的个数 e<nlogn则称作稀疏图,
否则称稠密图。
• 权——把图的边或弧赋予一个有意义的数。
• 网——带权图: 弧或边带权的图分别称作有向网或无
向网。
• 子图——如果图G(V,E)和图G’ (V’,E’),满足:V’V且
E’E,则称G’为G的子图。
• 邻接点——若无向图G中存在(V,W),则称V,W互为邻
接点; 边(V,W)和顶点V,W相关联。
图的定义和术语
• 顶点的度TD
–无向图中,顶点的度为与该顶点相连的边数
–有向图中,顶点的度分成入度与出度
入度ID:以该顶点为弧头的弧的数目
出度OD:以该顶点为弧尾的弧的数目
• 在无向图中,所有顶点度的和是图中边的2倍。
即∑TD(vi)=2e,i=1, 2, …, n ,e为图的边数
2
2
例
A
15
1
1
3
3
有向完全图
11
B
无向完全图
9
7
21
C
2
E
3
例
2
4
1
5
5
3
6
图G与子图G’
G=(V,E)
例
1
5
3
2
7
4
G2
顶点5的度:3
顶点2的度:4
6
3
6
G’=(V’,E’)
例
2
4
1
F
有向网
5
3
6
G1
顶点2入度:1
顶点4入度:1
出度:3
出度:0
8
图的定义和术语
路径:在图 G=(V, E) 中, 若从顶点 vi 出发, 沿一些边经过一些顶
点 vp1, vp2, …, vpm,到达顶点vj。则称顶点序列 ( vi vp1
vp2 ... vpm vj ) 为从顶点vi 到顶点 vj 的路径。它经过的边(vi,
vp1)、(vp1, vp2)、...、(vpm, vj)应当是属于E的边。
非带权图的路径长度是指此路径上边的条数;
路径长度: 带权图的路径长度是指路径上各边的权之和。
简单路径:路径上各顶点 v1,v2,...,vm 均不互相重复。
回路:
若路径上第一个顶点 v1 与最后一个顶点vm 重合,
则称这样的路径为回路或环。
2
1
4
3
5
6
路径示例
2
4
1
5
3
6
G1
1
3
5
2
7
4
G2
6
路径1→3:1 2 3 (5 6 3)
路径长度:2 (5)
简单路径:1,2,3,5
回路:1,2,3,5,6,3,1
简单回路:3,5,6,3
路径:1,2,5,7,6,5,2,3
路径长度:7
简单路径:1,2,5,7,6
回路:1,2,5,7,6,5,2,1
简单回路:1,2,3,1
10
连通图
• 在无向图中, 若从顶点vi到顶点vj有路径, 则称顶点vi与
vj是连通的。
• 如果图中任意一对顶点都是连通的, 则称此图是连通图。
• 非连通图的连通子图叫做连通分量。
强连通图
• 在有向图中, 若对于每一对顶点vi和vj, 都存在
一条从vi到vj和从vj到vi的路径, 则称此图是强连
通图。
• 非强连通图的强连通子图叫做强连通分量。
图的生成树
• 假设一个连通图有 n 个顶点和 e 条边,其n 个顶
点和其中 n-1 条边构成一个极小连通子图,称该
极小连通子图为此连通图的生成树。
• 关于无向图的生成树的几个结论:
– 一棵有n个顶点的生成树有且仅有n-1条边;
– 如果一个图有n个顶点和小于n-1条边,则是非连通图
– 如果多于n-1条边,则一定有环;
– 有n-1条边的图不一定是生成树。
图的生成森林
• 对非连通图,则称由各个连通分量的生成
树的集合为此非连通图的生成森林。
带权图——网
• 带权的图称为网。网是工程上常用的一个
概念,用来表示一个工程或某种流程。
a
6
b
3
3
2
e
4
c
1
5
d
图的抽象数据类型表示
….
7.2 图的存储结构
• 图的存储结构比较复杂:
– 任意顶点之间可能存在联系,无法以数据元素在存储
区中的物理位置来表示元素之间的关系。
– 图中顶点的度不一样,有的可能相差很大,若按度数
最大的顶点设计结构,则会浪费很多存储单元,反之
按每个顶点自己的度设计不同的结构,又会难以实现
• 常用存储方式
–
–
–
–
图的数组存储表示——邻接矩阵
图的邻接表存储表示
有向图的十字链表存储表示
无向图的邻接多重表存储表示
图的数组存储表示——邻接矩阵
• 对于有n个顶点的图,用一维数组vexs[n]存储顶点信
息,用二维数组A[n][n]存储顶点之间关系的信息,该
二维数组称为邻接矩阵。在邻接矩阵中,以顶点在
vexs数组中的下标代表顶点,邻接矩阵中的元素
A[i][j]存放的是顶点i到顶点j之间关系的信息。
• 定义:设G=(V,E)是有n1个顶点的图,G的邻接矩阵
A(二维数组)是具有以下性质的n阶方阵:
1, 若(v i , v j )或 v i , v j E(G)
A[i ][ j ]
0,其它
邻接矩阵示例
邻接矩阵特点
• 无向图的邻接矩阵对称,可压缩存储;有n
个顶点的无向图需存储空间为n(n+1)/2
• 有向图邻接矩阵不一定对称;有n个顶点的
有向图需存储空间为n2
• 无向图中
– 顶点Vi的度TD(Vi)是邻接矩阵A中第i行元素之和
• 有向图中,
– 顶点Vi的出度是A中第i行元素之和
– 顶点Vi的入度是A中第i列元素之和
网的邻接矩阵
•
网G=(V,E) 的邻接矩阵定义如下:
Wij 若<vi , vj>或(vi , vj)VR
∞ 反之
A[i][j]=
a
6
3
2
c
1
b 3
4
5
d
(a) 带权无向图
e
vexs
a
b
c
d
e
(b) 顶点矩阵
无向带权图的数组存储
∞
6
2
∞
∞
6 2 ∞
∞ 3 4
3 ∞ 1
4 3 ∞
3 ∞ 5
∞
3
∞
5
∞
(c) 邻接矩阵
网的邻接矩阵
a 6
2
c
3
1
b 3
4
5
d
(a) 带权有向图
e
vexs
a
b
c
d
e
(b) 顶点矩阵
∞
∞
∞
∞
∞
6 2
∞∞
3 ∞
4 ∞
∞∞
∞ ∞
∞ 3
1 ∞
∞ 5
∞ ∞
(c) 邻接矩阵
带权有向图的数组存储
• 邻接矩阵优点:容易实现图的操作,如:求某顶点的
度、判断顶点之间是否有边(弧)、找顶点的邻接点
等等。
• 邻接矩阵缺点: n个顶点需要n*n个单元存储边(弧);空
间效率为O(n2)。 对稀疏图而言尤其浪费空间。
图的邻接表存储表示
• 基本思想:对图的每个顶点建立一个单链表,第i
个单链表中的结点表示依附于顶点Vi的边(有向图
中指以Vi为尾的弧)。
• 每个链表附设一个表头结点。表头结点顺序存储,
以便随机访问任一顶点的链表。
• 每个表结点由三个域组成:其中邻接点域adjvex指
示与定点vi邻接的点在图中的位置,链域nextarc指
示下一条边或弧的结点;数据域info存储和边或弧
相关的信息,如权值等。
邻接表举例
邻接表示例
• 例1
邻接表
v2
v1
v3
v4
v5
• 例2
v1
0 v1
3
1
1 v2
2 v3
3 v4
4
2
0
^
4
3
1
^
4
2
0
^
4 v5
3
2
1
^
邻接表(出边)
v2
V1
2
V2 ^
v3
v4
^
逆邻接表(入边)
1 ^
V1
3 ^
V2
0 ^
V3
3 ^
V3
0 ^
V4
0 ^
V4
2 ^
注:邻接表不唯一,因各个边结点的链入顺序是任意的。
例3:已知某网的邻接(出边)表,请画出该网络。
1
80
64
5
对应于一个邻接表的图是唯一确定的
2
邻接表特点
• 无向图中
– 顶点Vi的度为第i个单链表中的结点数
• 有向图中
– 顶点Vi的出度为第i个单链表中的结点个数
– 顶点Vi的入度为整个单链表中邻接点域值是i的
结点个数(以vi为弧头)。
• 邻接矩阵多用于稠密图的存储;而邻接表
多用于稀疏图的存储
有向图的十字链表存储表示
• 十字链表(Orthogonal List)是有向图的另一种链
式存储结构,是将有向图的正邻接表和逆邻接表
结合起来得到的一种链表。
• 十字链表中,对应有向图的每一条弧有一个结点
,对应于每个顶点也有一个结点。结点结构如下
所示:
• 结点各字段表示含义如下:
十字链表结点结构
headvex ±íʾ» ¡Í·¶¥µã
ÔÚͼÖеÄλÖÃ
tailvex
headvex
tailvex ±íʾ» ¡Î²¶¥µã
ÔÚͼÖеÄλÖÃ
tlinkÖ¸ÏòÓë´Ë»¡µÄ» ¡
βÏàͬµÄÏÂÒ» Ìõ»¡
hlink
tlink
hlinkÖ¸ÏòÓë´Ë» ¡µÄ»¡
Í·ÏàͬµÄÏÂÒ» Ìõ»¡
(a) Ê®×ÖÁ´±í»¡½áµã½á¹¹Ê¾Òâ
ͼ7.11
firstinÓò(Á´Óò)ÓÃÓÚÖ¸ÏòÒԸö¥µã×÷
Ϊ» ¡Í·µÄµÚÒ» ¸ö»¡¶¥µã
data
firstin
firstout
dataÓòÓÃÓÚ´æ´¢Ó붥µãÓйصÄÐÅÏ¢,Èç
firstoutÓò(Á´Óò)ÓÃÓÚÖ¸ÏòÒԸö¥µã
¶¥µãµÄÃû×ÖµÈ
×÷Ϊ» ¡Î²µÄµÚÒ» ¸ö»¡¶¥µã
(b) Ê®×ÖÁ´±í¶¥µã½áµã½á¹¹Ê¾Òâ
十字链表示例
无向图的邻接多重表存储表示
• 邻接多重表(Adjacency Multilist)是无向图的另一
种链式存储结构。
• 虽然邻接表是无向图的一种有效的存储结构,在
无向图的邻接表中,一条边(v,w)的两个表结点分
别初选在以v和w为头结点的链表中,涉及到边的
操作不方便,例如删除一条边。
• 邻接多重表的结构和十字链表类似,每条边用一
个结点表示。
• 邻接多重表中的顶点结点结构与邻接表中的顶点
结构相同。
邻接多重表举例
邻接多重表
邻接表
邻接多重表与邻接表
• 区别:邻接表的同一条边用两个结点表示
,而邻接多重表只用一个结点表示。
• 联系:
– 除标志域外,邻接多重表与邻接表表达的信息
是相同的
– 邻接多重表与邻接表的操作实现也基本相似。
7.3 图的遍历
• 图的遍历:从连通图中某一顶点出发,沿着边访
遍图中所有的顶点,且使每个顶点仅被访问一次
• 图的遍历比较复杂,为什么?
• 图的任意顶点可能和其余的顶点相邻接,可能在
访问了某个顶点后,沿某条路径搜索后又回到原
顶点。
• 解决办法:在遍历过程中记下已被访问过的顶点
。设置一个辅助向量Visited[1…n](n为顶点数)
,其初值为0,一旦访问了顶点vi后,使
Visited[i]为1或为访问的次序。
• 图的遍历算法分为深度优先搜索算法和广度优先
搜索算法。
图的深度优先遍历
• 图的深度优先遍历类似树的先序遍历。
• 算法思想:
设初始状态时图中的所有顶点未被访问,则:
– ⑴ :从图中某个顶点vi出发,访问vi;然后找到vi的一
个邻接顶点vi1 ;
– ⑵:从vi1出发,深度优先搜索访问和vi1相邻接且未被
访问的所有顶点;
– ⑶:转⑴ ,直到和vi相邻接的所有顶点都被访问为止
– ⑷ :继续选取图中未被访问顶点vj作为起始顶点,转
第一步,直到图中所有顶点都被访问为止。
图的深度优先遍历示例
• 请给出图G4的深度优先遍历序列
• v1-->v2-->v4-->v8-->v5-->v3--> v6-->v7
图的深度优先遍历算法
图的广度优先遍历
• 图的广度优先遍历类似树的按层次遍历。
• 算法思想:
设初始状态时图中的所有顶点未被访问,则:
– ⑴:从图中某个顶点vi出发,访问vi;
– ⑵:访问vi的所有相邻接且未被访问的所有顶点vi1,vi2,
…,vim;
– ⑶:以vi1,vi2, …,vim的次序,以vij(1≦j≦m)依此作为vi
,转⑴;
– ⑷:继续选取图中未被访问顶点vk作为起始顶点,转⑴,
直到图中所有顶点都被访问为止。
图的广度优先遍历示例
• 请给出图G4的广度优先遍历序列
• v1-->v2-->v3-->v4-->v5-->v6--> v7-->v8
图的广度优先遍历算法
7.4 图的连通性问题
• 对于无向图,对其进行遍历时:
– 若是连通图:仅需从图中任一顶点出发,就能访问图
中的所有顶点;
– 若是非连通图:需从图中多个顶点出发。每次从一个
新顶点出发所访问的顶点集序列恰好是各个连通分量
的顶点集;
例:对下图进行深度优先遍历,得到的顶点序列为
ALMJBFC
DE
GKHI
连通图与生成树
• 对连通图进行遍历,得到的是什么?
– 得到的将是一个极小连通子图,即图的生成树
– 由深度优先搜索得到的生成树,称为深度优先
搜索生成树。
– 由广度优先搜索得到的生成树,称为广度优先
生成树。
• 请画出图G4的深度优先生成树和深度优
先生成树
生成树
非联通图与生成森林
• 对非连通图进行遍历,得到的是什么?
• 每个连通分量中的顶点集,和遍历时走过
的边一起构成若干棵生成树,这些连通分
量的生成树组成非连通图的生成森林。
• 请画出下图的生成森林?
生成树算法
7.4.2 有向图的强连通分量
• 对于有向图,在其每一个强连通分量中,
任何两个顶点都是可达的。 VG,与V
可相互到达的所有顶点就是包含V的强连通
分量的所有顶点。
• 设从V可到达的顶点集合为T1(G),而可到
达V 的顶点集合为T2(G),则包含V的强连
通分量的顶点集合是: T1(G)∩T2(G) 。
例:求有向图G的强连通分量
• 下图是求有向图的强连通分量过程。
a 6
a
b
c
d
b 3
2 f
e
f
(a) 有向图G
e 1
a
c 5
d 4
b
b
d
e
c
f
c
d
e
a
f
(c) 图中边反向
(d) 再次深度优先遍历
(b) 从a出发深度优先遍历
7.3 最小生成树
• 最小生成树在实际中具有重要用途,如设计通信
网。图的顶点表示城市,边表示两个城市之间的
通信线路,边的权值表示建造通信线路的费用。
n个城市之间最多可以建n(n-1)/2条线路,如何选
择其中的n-1条,使所有城市连通且总的建造费
用最低?
• 带权图的生成树中,所有边的权值之和称为生成
树的代价。
• 最小生成树(Minimum Spanning Tree) :带权连
通图中代价最小的生成树称为最小生成树。
最小生成树
• 构造最小生成树的算法有多种,基本原则:尽可
能选取权值最小的边,但不能构成回路。
• 多数算法都利用了最小生成树的MST性质:
– 设G=(V,E)是一个带权连通图,U是顶点集V
的一个非空子集。若u∈U ,v∈V-U,且(u, v)是
U中顶点到V-U中顶点之间权值最小的边,则
必存在一棵包含边(u, v)的最小生成树。
MST性质证明
• 证明: 用反证法证明。
• 设图G的任何一棵最小生成树都不包含边(u,v)。设T是G
的一棵最小生成树,从u到v必有一条路径(u,…,v)。当将
边(u,v)加入到T中时就构成了回路。则路径(u, …,v)中必
有一条边(u’,v’) ,其中u’∈U ,v’∈V-U 。删去边(u’,v’) 便
可消除回路,同时得到另一棵生成树T’。
• 由于(u,v)是U中顶点到V-U中顶点之间权值最小的边,故
(u,v)的权值不会高于(u’,v’)的权值,T’的代价也不会高于T
, T’是包含(u,v) 的一棵最小生成树,与假设矛盾。
最小生成树的Prim算法
• 假设N =(V , E)是连通网,TE是N上最小生成
树的边集。
– 从初始状态 U ={u0}(u0∈V),TE={ }开始,重复执行以下
操作:
– 在所有u∈U、v∈V-U的边(u,v)∈E中找一条代价最小的
边(u0, v0)并入集合TE,同时将顶点v0并入集合U,直
到U=V为止。
– 此时TE中必有n-1条边,T=(V,{TE})为N的最小生成
树。
例:Prim算法构成最小生成树
例:Prim算法构成最小生成树
v2
8
v1
5
4
v4
7
12
11
3
v2
v3
v2
5
6
5
v4
v5
(a)
v4
(c)
(b)
v2
v1
v2
v1
5
4
v4
3
3
(d)
v5
v3
5
4
v4
6
3
(e)
v5
v5
Prim算法实现
• 设用邻接矩阵表示图。
• 需设置一个数组closedge[n],用来记录从U到VU中具有最小代价的边。对每个顶点vi∈V-U,辅
助数组中存在相应分量closedge[i-1]。
• closedge数组的类型定义是:
struct
{
int adjvex ; /* 边所依附于U中的顶点 */
int lowcost ; /* 该边的权值 */
} closedge[MAX_EDGE] ;
closedge数组的变化
Prim算法
最小生成树Kruskal算法
• 设N = { V, E }是有 n 个顶点的连通网,
– 首先构造一个只有 n 个顶点但没有边的非连通图 T = {
V, {} }, 图中每个顶点自成一个连通分量。
– 在 E 中选到一条具有最小权值的边,若该边的两个顶点
落在T中不同的连通分量上,则将此边加入到生成树的
边集合T 中;否则将此边舍去,重新选择一条权值最
小的边。
– 如此重复下去,直到所有顶点在同一个连通分量上为
止。此时的T即为所求(最小生成树)。
例:Kruskal算法
例:Kruskal算法
v2
8
v1
5
4
v4
7
12
11
3
v3
v1
6
v5
(a)
v4
3
4
v5
v4
(c)
(b)
v2
v1
3
v2
v1
5
4
3
v4
(d)
v5
v3
5
4
v4
6
3
(e)
v5
v5
Kruskal算法的实现
• 这个算法实现的难点在于?
– 如何判断该边的两个顶点落在T中不同的连通
分量上
• 解决办法?
– 定义一个一维数组Vset[n] ,存放图T中每个顶
点所在的连通分量的编号。
– 当往T中增加一条边(u,v) 时,先检查Vset[u]
和Vset[v]值
上机练习
• 用Kruskal 算法求出P174页图7.16中图a的
最小生成树。
7.5 有向无环图及其应用
• 有向无环图(Directed Acycling Graph):是
图中没有回路的有向图。
• 有向无环图是一类具有代表性的图,用途
多多。
有向无环图应用一:描述表达式
• 有向无环图可用于描述含有公共子式的表
达式,例如
( (a+b)*(b*(c+d))+(c+d)*e)*((c+d)*e)
有向无环图应用二:描述工程
• 有向无环图还可用于研究工程项目的工序问题、工程时间
进度问题等。
• 一个工程(project)都可分为若干个称为活动(active)的子工
程(或工序),各个子工程受到一定的条件约束:某个子工
程必须开始于另一个子工程完成之后;整个工程有一个开
始点(起点)和一个终点。人们关心:
– 工程能否顺利完成?影响工程的关键活动是什么?
– 估算整个工程完成所必须的最短时间是多少?
•
对工程的活动加以抽象:图中顶点表示活动,有向边表
示活动之间的优先关系,这样的有向图称为顶点表示活动
的网(Activity On Vertex Network ,AOV网) 。
7.5.1 拓扑排序
• 集合上的关系回顾:
– 自反性:若 a∈A有(a,a)∈R,称集合A上的关系
R是自反的。
– 对称性:如果对于a,b∈A ,只要有(a,b)∈R就有
(b,a)∈R ,称集合A上的关系R是对称的。
– 反对称性:如果对于a,b∈A ,仅当a=b时有(a,
b)∈R和(b,a)∈R ,称集合A上的关系R是反对称
的。
– 传递性:若a,b,c∈A,若(a,b)∈R,并且(b,
c)∈R ,则(a,c)∈R ,称集合A上的关系R是传递
的。
偏序与全序
• 偏序:若集合A上的关系R是自反的,反对称的和
传递的,则称R是集合A上的偏序关系。
• 全序:设R是集合A上的偏序关系,a,b∈A,必
有aRb或bRa, 则称R是集合A上的全序关系。
• 偏序是指集合中仅有部分元素之间可以比较,而
全序是指集合中任意两个元素之间都可以比较。
拓扑排序
• 拓扑排序(Topological Sort) :由某个集合
上的偏序关系得到该集合上的全序关系。
• 拓扑排序的应用
– 在AOV网中,若有有向边<i, j>,则i是j的直接
前驱,j是i的直接后继;若从顶点i到顶点j有有
向路径,则i是j的前驱,j是i的后继。
– AOV网中不能有环,否则,某项活动能否进行
是以自身的完成作为前提条件。
– 检查方法:对有向图的顶点进行拓扑排序,若
所有顶点都在其拓扑有序序列中,则无环。
拓扑排序
• 有向图的拓扑排序:构造AOV网中顶点的
一个拓扑线性序列(v’1,v’2, ⋯,v’n),使得该
线性序列不仅保持原来有向图中顶点之间
的优先关系,而且对原图中没有优先关系
的顶点之间也建立一种(人为的)优先关系。
例:拓扑排序
• 该图的拓扑有序序列:(不唯一)
(c1,c2,c3,c4,c5,c7,c9,c10,c11,c6,c12,c8)
(c9,c10,c11,c6,c1,c12,c4,c2,c3,c5,c7,c8)
用户应按照这样的顺序推进工程。
如何进行拓扑排序?
• 在AOV网中选择一个没有前驱的顶点且输出;
• 在AOV网中删除该顶点以及从该顶点出发的所有
有向边 ;
• 重复前面两步,直到图中全部顶点都已输出或图
中不存在无前驱的顶点。
• 图中全部顶点都已输出表示?
– 图中无环
• 图中不存在无前驱的顶点表示?
– 图中存在环
例: 拓扑排序
v1
v2
v4
v3
v4
v3
v6
v5
v6
(a) 有向图
v2
v2
v2
v3
v3
v5
v5
v5
v5
(b) 输出v1后
(c) 输出v6后
(d) 输出v4后
(e) 输出v3后
v4
图7-28 有向图的拓扑排序过程
v2
拓扑排序的实现
• 假设采用邻接表作为有向图的存储结构,
并且头结点中有用于存放顶点入度的数组
indegree。
• 如何查找没有前驱的结点?
– 入度为0的数组就是没有前驱的结点
• 如何删除顶点及以它为尾的弧?
– 将弧头顶点的入度减1
• 为了避免每次检测哪些结点入度为0,设
置一个栈S来存放所有入度为0的结点。
7.5.2 关键路径
• 与AOV网相对应的是AOE(Activity On Edge) ,
用边表示活动。
• 图中顶点表示事件(Event),每个事件表示在其前
的所有活动已经完成,其后的活动可以开始;弧
表示活动,弧上的权值表示相应活动所需的时间
。
关于AOE网
• 与AOE有关的研究问题
– 完成整个工程至少需要多少时间?
– 哪些活动是影响工程进度的关键?
关键路径
• 由于AOE网中有些活动可以并行进行,所
以完成工程的最短时间是从开始点到完成
点的最长路径的长度。长度最长的路径称
为关键路径,关键路径上的活动称为关键
活动。关键活动是影响整个工程的关键。
• 设v0是起点,从v0到vi的最长路径长度称为
事件vi的最早发生时间。这个时间决定了所
有以vi为尾的所有活动的最早开始时间。
关键活动
• 用e(i)表示表示活动ai的最早开始时间;再
定义 l(i)表示表示活动li的最迟开始时间;
l(i)-e(i)表示活动ai的时间余量。若l(i)-e(i)=0
,则活动ai是关键活动。
• 显然,关键路径上的所有活动都是关键活
动,提前完成非关键活动不能加快工程进
度。
例:关键路径和关键活动
• 找出下图的关键路径:
• 为什么说活动a6可以推迟3天开始或延迟3
天结束?
关键路径的应用
• 分析关键路径的目的是找出关键应用,争
取提高关键活动的功效,缩短整个工期。
• 辨别关键活动——找出e(i)= l(i)的活动。要
求e(i)和 l(i),首先需要求出:
– ve(i):表示事件的最早发生时间,即从起点到
顶点vi的最长路径长度;
– vl(i):表示事件的最晚发生时间。
关键路径的应用
• 如果活动ai由弧<j,k>表示,其持续时间为
dut(<j,k>),则有
e(i)=ve(j)
l(i)= vl(k)-dut(<j, k>)
• 求ve(j)和vl(j)分两步进行
− 从ve(0)开始向前递推
ve(j)=Max{ve(i)+dut(<i, j>) } <i, j> ∈T
T是所有以j为头的弧的集合
− 从vl(n-1)=ve(n-1)起向后递推
vl(i)=Min{vl(j)-dut(<i, j>)
<i,j> ∈S
S是所有以i为尾的弧的集合
关键路径的应用
• 这两个递推公式的计算必须分别在拓扑有
序和逆拓扑有序的前提下进行。即
– ve(j-1)必须在vj的所有前驱的最早发生时间求
出之后才能确定。
– vl(j-1)必须在vj的所有后继的最迟发生时间求得
之后才能确定。
• 因此可以在拓扑排序的基础上计算ve(j1)和vl(j-1)。
关键路径算法
• 利用拓扑排序求出AOE网的一个拓扑序列
• 从拓扑排序的序列的第一个顶点(源点)开始
,按拓扑顺序依次计算各顶点的最早发生
时间ve(i) ;
• 从拓扑排序的序列的最后一个顶点(汇点)开
始,按逆拓扑顺序依次计算每个顶点的最
晚发生时间vl(i) ;
• 根据各顶点的ve和vl值,求每条弧的最早
开始时间e(s)和最晚开始时间l(s)。找出所
有e(s)=l(s)的弧。
关键路径算法示例
• 求出下图的关键路径,要求步骤
7.7 最短路径
• 若用带权图表示交通网,图中顶点表示地
点,边代表两地之间有直接道路,边上的
权值表示路程(所花费用,或时间) 。从一
个地方到另一个地方的路径长度表示该路
径上各边的权值之和。问题:
– 两地之间是否有通路?
– 在有多条通路的情况下,哪条最短?
• 考虑到交通网的有向性,直接讨论的是带
权有向图的最短路径问题,但解决问题的
算法也适用于无向图。
最短路径
• 将一个路径的起始顶点称为源点,最后一
个顶点称为终点。
两种最常见的最短路径
• 从某个源点到其他各结点的最短路径
– 对于给定的有向图G=(V,E)及单个源点Vs,
求Vs到G的其余各顶点的最短路径。
单源点最短路径
• 针对单源点的最短路径问题,Dijkstra提出
了一种按路径长度递增次序产生最短路径
的算法,即迪杰斯特拉(Dijkstra)算法。
Dijkstra算法
• 按长度递增的次序生成各顶点的最短路径,即先求
出长度最小的一条最短路径,然后求出长度第二小
的最短路径,依此类推,直到求出长度最长的最短
路径。
• 设给定源点为Vs,S为已求得最短路径的终点集,
开始时令S={Vs} 。当求得第一条最短路径(Vs,Vi)
后,S为{Vs,Vi} 。然后再求下一条最短路径。设下
一条最短路径终点为Vj,则Vj只有两种可能:
– 源点到终点有直接的弧<Vs,Vj>;
– 从Vs 出发到Vj 的这条最短路径所经过的所有中间顶点
必定在S中。即只有这条最短路径的最后一条弧才是从S
内某个顶点连接到S外的顶点Vj 。
Dijkstra算法
• 数组D[i]表示当前从v到vi的最短路径的长度
– 初值:v到vi有边直接相连,则D[i]为边上的权
值;否则为∞。长度为D[j]=Min{D[i]|vi∈V}的路
径是从v出发的一条长度最短的路径。
– 一般来说,下一条长度次短的最短路径的长度
必是D[j]=Min{D[i]|vi∈V-S},其中D[i]或者是弧
(v,vi)上的权值,或者是D[k]+w(vk,vi),其中
k∈S。
例:Dijkstra算法
算法实现
• 令S={Vs} ,用带权的邻接矩阵表示有向图,对图中每个
顶点Vi按以下原则置初值:
0
dist[i]= Wsi
∞
i=s
i≠s且<vs,vi>∈E, wsi为弧上的权值
i≠s且<vs,vi>不属于E
• 选择一个顶点Vj ,使得:dist[j]=Min{ dist[k]| Vk∈V-S },
Vj就是求得的下一条最短路径终点,将Vj 并入到S中,即
S=S∪{Vj} 。
• 对V-S中的每个顶点Vk,若dist[j]+Wjk<dist[k],则修改为
dist[k]=dist[j]+Wjk (Vk∈V-S )
• 重复以上两步,直到S=V为止。
两种最常见的最短路径
• 每一对顶点之间的最短路径
• 解决办法一:用Dijkstra算法求出每个顶点
到其余顶点的最短路径
• 解决办法二:Floyd算法