第6章 图
【例6-1】回答下列问题:
(1)具有n个顶点的连通图至少有多少条边?
(2)具有n个顶点的强连通图至少有多少条边?这样的图应该是什么形状?
(3)具有n个顶点的有向无环图最多有多少条边?
解:
(1)具有n个顶点的连通图至少有n-1条边。
这是一个与生成树相关的问题。生成树是一个连通图,它具有能够连通图中任何两个顶点的最小边集,任何一个生成树都具有n-1边。因此,具有n个顶点的连通图至少有n-1条边。
(2)具有n个顶点的强连通图至少有n条边,这样的图是一个由n个顶点构成的环。
强连通图是相对于有向图而言的。由于强连通图要求图中任何两个顶点之间能够相互连通,因此每个顶点至少要有一条以该顶点为弧头的弧和一条以该顶点为弧尾的弧,每个顶点的入度和出度至少各为1,即顶点的度至少为2,这样根据图的顶点数、边数以及各项点的度三者之间的关系计算可得:边数=2×n/2=n。
(3)具有n个顶点的有向无环图最多有n×(n—1)/2条边。
这是一个拓扑排序相关的问题。—个有向无环图至少可以排出一个拓扑序列,不妨设这n个顶点排成的拓扑序列为v1,v2,v3,…,vn,那么在这个序列中,每个顶点vi只可能与排在它后面的顶点之间存在着以vi为弧尾的弧,最多有n-i条,因此在整个图中最多有(n-1)+(n-2)+ … +2+1=n×(n-1)/2条边。
2.图的存储结构
常用的存储结构有邻接矩阵和邻接表。
(1)邻接矩阵表示法
设G=(V,E)是有n(n≥1)个顶点的图。则G的邻接矩阵是按如下定义的n阶方阵:
例如,图6-1中G1,G2的邻接矩阵分别表示为A1、A2,矩阵的行列号对应于图6-1中结点的序号。
由邻接矩阵的定义可知,无向图的邻接矩阵必定是对称阵;有向图的邻接矩阵不一定是对称的。
根据邻接矩阵,很容易判定任意两个顶点之间是否有边相连。求各顶点的度也是非常容易的。对于无向图,顶点Vi的度就是邻接矩阵中第i行(或第j列)上非零元的个数,即。对于有向图,第i行中非零元的个数为顶点Vi的出度,而第i列上的非零元个数为顶点Vi的入度。
(2)邻接表表示法
图的邻接链表存储结构是一种顺序分配和链式分配相结合的存储结构括两个部分:一部分是向量,另一部分是链表。
邻接链表中的表头部分是向量,用来存储n个表头结点。向量的下标指示顶点的序号。
例如,对于图6-1中G1和G2,其邻接链表如图6-3所示。
在无向图的邻接表中顶点vi的度就是第i个链表中结点的个数。在有向图中,第i个链表的结点数仅是vi的出度,求vi的入度,必须查遍n个链表才能得出。
【例6-2】 图G=(V,E),其中V={1,2,3,4,5,6},E={<1,2>,<1,3>,<1,4>,<2,5>,<3,2>,<3,5>,
<3,6>,<4,6>,<5,6>},请画出图G,并写出其邻接矩阵和邻接表表示。
解:图G如图6-4中的(a)所示,图G的邻接矩阵和邻接表表示分别如图(b)和(c)所示。
对于这类问题,只要掌握了图的概念和存储结构就可以做出正确的答案。通常情况下.对图的顶点排列顺序和各顶点的邻接点排列顺序并没有特定要求,因此,在写出邻接矩阵和邻接表表示时,只要按照某种排列顺序画出相应的结构图就可以了。但应该注意的是,对于邻接矩阵表示,如果顶点结点的顺序不同,那么邻接矩阵就不相同;对于邻接表表示,如果顶点结点的顺序或者邻接点的顺序不同,那么邻接表就不相同。
【例6-3】已知一个无向图的邻接表如图6-5所示,要求:
(1)画出该无向图;
(2)根据邻接表,分别写出用DFS(深度优先搜索)和BFS(广度优先搜索)算法从顶点V0开始遍历该图后所得到的遍历序列。
解:
(1)该无向图如图6-6所示。
(2)根据该无向图的邻接表表示,从顶点V0开始的深度优先遍历序列为:V0、V2、V3、V1、V4、V6、V5。广度优先遍历序列为V0、V2、V5、V6、V1、V3、V4。
从图的逻辑结构上来讲,从图中某个顶点开始的深度(或广度)优先遍历序列不一定是唯一的。这是因为在逻辑结构中,并没有对每个顶点的所有邻接点规定它们之间的先后顺序,这样在搜索算法中选取第—个邻接点和下一个邻接点时可能会有不同的结果。但是在存储结构中,明确地给出了邻接点的先后顺序,这时深度优先和广度优先遍历序列就是唯一的。
【例6-4】对于如图6-8所示的带权无向图,用图示说明:
(1)利用Prim算法从顶点a开始构造最小生成树的过程;
(2)利用Kruskal算法构造最小生成树的过程;//K算法是挑边
解:
(1)利用Prim算法从顶点a开始构造最小生成树的过程如图6-9所示。
(2)利用Kruskal算法构造最小生成树的过程如图6-10所示。
【例6-5】 一个带权无向图的最小生成树是否一定唯一?在什么情况下构造出的最小生成树可能不唯一?
解:一个带权无向图的最小生成树不一定是唯一的。从Kruskal算法构造最小生成树的过程可以看出,当从图中选择当前权值最小的边时,如果存在多条这样的边,并且这些边与已经选取的边构成回路,此时这些边就不可能同时出现在一棵最小生成树中,对这些边的不同选择结果可能会产生不同的最小生成树。
习题6
一、单项选择题
1. 在一个具有n个顶点的有向图中,若所有顶点的出度数之和为s,则所有顶点的入度数之和为(A )。
A. s B. s-1 C. s+1 D. n
2. 在一个具有n个顶点的有向图中,若所有顶点的出度数之和为s,则所有顶点的度数之和为( D )。//度数之=入度+出度
A. s B. s-1 C. s+1 D. 2s
3. 在一个具有n个顶点的无向图中,若具有e条边,则所有顶点的度数之和为(D )。
A. n B. e C. n+e D. 2e
4. 在一个具有n个顶点的无向完全图中,所含的边数为(C)。
A. n B. n(n-1) C. n(n-1)/2 D. n(n+1)/2
5. 在一个具有n个顶点的有向完全图中,所含的边数为( B )。
A. n B. n(n-1) C. n(n-1)/2 D. n(n+1)/2
6. 在一个无向图中,若两顶点之间的路径长度为k,则该路径上的顶点数为(B )。
A. k B. k+1 C. k+2 D. 2k
7. 对于一个具有n个顶点的无向连通图,它包含的连通分量的个数为(B)。
//无向连通图的连通分量=它的极大连通分量
A. 0 B. 1 C. n D. n+1
8. 若一个图中包含有k个连通分量,若要按照深度优先搜索的方法访问所有顶点,则必须调用(A )次深度优先搜索遍历的算法。
A. k B. 1 C. k-1 D. k+1
9. 若要把n个顶点连接为一个连通图,则至少需要(C )条边。
A. n B. n+1 C. n-1 D. 2n
10. 在一个具有n个顶点和e条边的无向图的邻接矩阵中,表示边存在的元素(又称为有效元素)的个数为( D )。
A. n B. n´e C. e D. 2´e
11. 在一个具有n个顶点和e条边的有向图的邻接矩阵中,表示边存在的元素个数为( C )。
A. n B. n´e C. e D. 2´e
12. 在一个具有n个顶点和e条边的无向图的邻接表中,边结点的个数为(D )。
A. n B. n´e C. e D. 2´e
//总之,只要看到无向图、边结点,答案就是2e
13. 在一个具有n个顶点和e条边的有向图的邻接表中,保存顶点单链表的表头指针向量的大小至少为(A )。
A. n B. 2n C. e D. 2e
14. 在一个无权图的邻接表表示中,每个边结点至少包含(B )域。//数据与和指针域
A. 1 B. 2 C. 3 D. 4
15. 对于一个有向图,若一个顶点的度为k1,出度为k2,则对应邻接表中该顶点单链表中的边结点数为(B )。//邻接表的边结点数=出度
A. k1 B. k2 C. k1-k2 D. k1+k2
16. 对于一个有向图,若一个顶点的度为k1,出度为k2,则对应逆邻接表中该顶点单链表中的边结点数为(C )。//逆邻接表,后边跟的是入度,入度=度-出度
A. k1 B. k2 C. k1-k2 D. k1+k2
17. 对于一个无向图,下面(A )种说法是正确的。
A. 每个顶点的入度等于出度 B. 每个顶点的度等于其入度与出度之和
C. 每个顶点的入度为0 D. 每个顶点的出度为0
18. 在一个有向图的邻接表中,每个顶点单链表中结点的个数等于该顶点的(A)。
A. 出边数 B. 入边数 C. 度数 D. 度数减1
//19.20有关图的遍历,画图,因为,可能
19. 若一个图的边集为{(A,B),(A,C),(B,D),(C,F),(D,E),(D,F)},则从顶点A开始对该图进行深度优先搜索,得到的顶点序列可能为( B )。
A. A,B,C,F,D,E B. A,C,F,D,E,B
C. A,B,D,C,F,E D. A,B,D,F,E,C
20. 若一个图的边集为{(A,B),(A,C),(B,D),(C,F),(D,E),(D,F)},则从顶点A开始对该图进行广度优先搜索,得到的顶点序列可能为(D )。
A. A,B,C,D,E,F B. A,B,C,F,D,E
C. A,B,D,C,E,F D. A,C,B,F,D,E
21. 若一个图的边集为{<1,2>,<1,4>,<2,5>,<3,1>,<3,5>,<4,3>},则从顶点1开始对该图进行深度优先搜索,得到的顶点序列可能为(A )。
A. 1,2,5,4,3 B. 1,2,3,4,5
C. 1,2,5,3,4 D. 1,4,3,2,5
22. 若一个图的边集为{<1,2>,<1,4>,<2,5>,<3,1>,<3,5>,<4,3>},则从顶点1开始对该图进行广度优先搜索,得到的顶点序列可能为( C )。
A. 1,2,3,4,5 B. 1,2,4,3,5
C. 1,2,4,5,3 D. 1,4,2,5,3
23. 由一个具有n个顶点的连通图生成的最小生成树中,具有(B )条边。
A. n B. n-1 C. n+1 D. 2´n
24. 已知一个有向图的边集为{<a,b>,<a,c>,<a,d>,<b,d>,<b,e>,<d,e>},则由该图产生的一种可能的拓扑序列为(24. A )。//找入度为0的边依次输出
A. a,b,c,d,e B. a,b,d,e,b C. a,c,b,e,d D. a,c,d,b,e
二、填空题
1. 在一个图中,所有顶点的度数之和等于所有边数的____2 ____倍。
2. 在一个具有n个顶点的无向完全图中,包含有_____ n(n-1)/2___条边,在一个具有n个顶点的有向完全图中,包含有____ n(n-1)____条边。
3. 假定一个有向图的顶点集为{a,b,c,d,e,f},边集为{<a,c>, <a,e>, <c,f>, <d,c>, <e,b>, <e,d>},则出度为0的顶点个数为____2____,入度为1的顶点个数为__4 ______。
4. 在一个具有n个顶点的无向图中,要连通所有顶点则至少需要___ n-1 _____条边。
5. 表示图的两种存储结构为____邻接矩阵______和_____邻接表_____。
6. 在一个连通图中存在着_____1 ___个连通分量。
7. 图中的一条路径长度为k,该路径所含的顶点数为___ k+1_____。
8. 若一个图的顶点集为{a,b,c,d,e,f}边集为{(a,b),(a,c),(b,c),(d,e)},则该图含有3________个连通分量。//包含3个图
9. 对于一个具有n个顶点的图,若采用邻接矩阵表示,则矩阵大小至少为_______n_´__n______。
10. 对于具有n个顶点和e条边的有向图和无向图,在它们对应的邻接表中,所含边结点的个数分别为______ 2e __和_____ e ___。
11. 在有向图的邻接表和逆邻接表表示中,每个顶点邻接表分别链接着该顶点的所有_____出边___和_____入边___结点。
12. 对于一个具有n个顶点和e条边的无向图,当分别采用邻接矩阵和邻接表表示时,求任一顶点度数的时间复杂度分别为____ O(n)____和___ O(e/n)_____。
13. 假定一个图具有n个顶点和e条边,则采用邻接矩阵和邻接表表示时,其相应的空间复杂度分别为____ O(n2)____和____ O(n+e)____。
14. 一个图的边集为{(a,c),(a,e),(b,e),(c,d),(d,e)},从顶点a出发进行深度优先搜索遍历得到的顶点序列为________ acdeb ____,从顶点a出发进行广度优先搜索遍历得到的顶点序列为_______ acedb _____。 //acebd
15. 一个图的边集为{<a,c>,<a,e>,<c,f>,<d,c>,<e,b>,<e,d>},从顶点a出发进行深度优先搜索遍历得到的顶点序列为_______ acfebd _____,从顶点a出发进行广度优先搜索遍历得到的顶点序列为_______ acefbd ___。答案不唯一)//咱也不敢说答案,咱也算的不一样,痛苦ing
16. 图的_____深度___优先搜索遍历算法是一种递归算法,图的__广度______优先搜索遍历算法需要使用队列。
17. 对于一个具有n个顶点和e条边的连通图,其生成树中的顶点数和边数分别为_____ n ___和____ n-1____。
18. 若一个连通图中每个边上的权值均不同,则得到的最小生成树是____唯一____(唯一/不唯一)的。
19. 根据图的存储结构进行某种次序的遍历,得到的顶点序列是_唯一 _(唯一/不唯一)的
20. 假定一个有向图的边集为{<a,c>,<a,e>,<c,f>,<d,c>,<e,b>,<e,d>},对该图进行拓扑排序得到的顶点序列为__ aebdcf_____。 //拓扑排序就是选定入度为0的结点逐个输出
三、应用题
1. 对于一个无向图6-11(a),假定采用邻接矩阵表示,试分别写出从顶点0出发按深度优先搜索遍历得到的顶点序列和按广度优先搜索遍历得到的顶点序列。
注:每一种序列都是唯一的,因为都是在存储结构上得到的。
1. 深度优先搜索序列:0,1,2,8,3,4,5,6,7,9
广度优先搜索序列:0,1,4,2,7,3,8,6,5,9
2. 对于一个有向图6-11(b),假定采用邻接表表示,并且假定每个顶点单链表中的边结点是按出边邻接点序号从大到小的次序链接的,试分别写出从顶点0出发按深度优先搜索遍历得到的顶点序列和按广度优先搜索遍历得到的顶点序列。
注:每一种序列都是唯一的,因为都是在存储结构上得到的。
2. 深度优先搜索序列:0,4,7,5,8,3,6,1,2
广度优先搜索序列:0,4,3,1,7,5,6,2,8
3. 已知一个无向图的邻接矩阵如图6-12(a)所示,试写出从顶点0出发分别进行深度优先和广度优先搜索遍历得到的顶点序列。
3. 深度优先搜索序列:0,2,3,5,6,1,4
广度优先搜索序列:0,2,3,5,6,1,4
4. 已知一个无向图的邻接表如图6-12(b)所示,试写出从顶点0出发分别进行深度优先和广度优先搜索遍历得到的顶点序列。
4. 深度优先搜索序列:0,3,6,4,1,5,2
广度优先搜索序列:0,3,2,6,5,4,1
5. 已知图6-13所示的一个网,按照Prim方法,从顶点1 出发,求该网的最小生成树的产生过程。
5. 过程如图6-16所示
6. 已知图6-13所示的一个网,按照Kruskal方法,求该网的最小生成树的产生过程。
6. 求解过程如图6-17所示。
7. 图6-14所示为一个有向网图及其带权邻接矩阵,要求对有向图采用Dijkstra算法,求从V0 到其余各顶点的最短路径。
7. 求解过程如下表所示。
终点 |
从v0到各终点的D值和最短路径的求解过程 |
|||||
i=1 |
i=2 |
i=3 |
i=4 |
i=5 |
||
V1 |
∞ |
∞ |
∞ |
∞ |
∞ 无 |
|
V2 |
10 (v0,v2) |
|
||||
|
||||||
V3 |
∞ |
60 (v0,v2,v3) |
50 (v0,v4,v3) |
|
||
|
||||||
V4 |
30 (v0,v4) |
30 (v0,v4) |
|
|||
|
||||||
V5 |
100 (v0,v5) |
100 (v0,v5) |
90 (v0,v4,v5) |
60 (v0,v4,v3,v5) |
|
|
|
||||||
Vj |
V2 |
V4 |
V3 |
V5 |
||
S |
{v0,v2} |
{v0,v2,v4} |
{v0,v2,v3,v4} |
{v0,v2,v3,v4,v5} |
8. 图6-15给出了一个具有15个活动、11个事件的工程的AOE网,求关键路径。
8. 求解过程如下:
①事件的最早发生时间ve[k]。
ve (1)=0
ve (2)=3
ve (3)=4
ve (4)=ve(2)+2=5
ve (5)=max{ve(2)+1,ve(3)+3}=7
ve (6)=ve(3)+5=9
ve (7)=max{ve(4)+6,ve(5)+8}=15
ve (8)=ve(5)+4=11
ve (9)=max{ve(8)+10,ve(6)+2}=21
ve (10)=max{ve(8)+4,ve(9)+1}=22
ve (11)=max{ve(7)+7,ve(10)+6}=28
②事件的最迟发生时间vl[k]。
vl (11)= ve (11)=28
vl (10)= vl (11)-6=22
vl (9)= vl (10)-1=21
vl (8)=min{ vl (10)-4, vl (9)-10}=11
vl (7)= vl (11)-7=21
vl (6)= vl (9)-2=19
vl (5)=min{ vl (7)-8,vl (8)-4}=7
vl (4)= vl (7)-6=15
vl (3)=min{ vl (5)-3, vl (6)-5}=4
vl (2)=min{ vl (4)-2, vl (5)-1}=6
vl (1)=min{vl (2)-3, vl (3)-4}=0
③活动ai的最早开始时间e[i]和最晚开始时间l[i]。
活动a1 e (1)=ve (1)=0 l (1)=vl (2) -3 =3
活动a2 e (2)=ve (1)=0 l (2)=vl (3) - 4=0
活动a3 e (3)=ve (2)=3 l (3)=vl (4) - 2=13
活动a4 e (4)=ve (2)=3 l (4)=vl (5) - 1=6
活动a5 e (5)=ve (3)=4 l (5)=vl (5) - 3=4
活动a6 e (6)=ve (3)=4 l (6)=vl (6) - 5=14
活动a7 e (7)=ve (4)=5 l (7)=vl (7) - 6=15
活动a8 e (8)=ve (5)=7 l (8)=vl (7) - 8=13
活动a9 e (9)=ve (5)=7 l (9)=vl (8) - 4=7
活动a10 e (10)=ve (6)=9 l (10)=vl (9) - 2=19
活动a11 e (11)=ve (7)=15 l (11)=vl (11) - 7=21
活动a12 e (12)=ve (8)=11 l (12)=vl (10) - 4=18
活动a13 e (13)=ve (8)=11 l (13)=vl (9) - 10=11
活动a14 e (14)=ve (9)=21 l (14)=vl (10) -1=21
活动a15 e (15)=ve (10)=22 l (15)=vl (11) -6 =22
④最后,比较e[i]和l[i]的值可判断出a2,a5,a9,a13,a14,a15是关键活动,关键路径如图6-18所示。
//现在是11点,这个关键路径已成功求得
四、算法设计题
1. 编写一个算法,求出邻接矩阵表示的无向图中序号为numb的顶点的度数。
int degree1(Graph & ga, int numb)
1. int degree1(Graph & ga, int numb)
{ //根据无向图的邻接矩阵求出序号为numb的顶点的度数
int j,d=0;
for(j=0; j<ga.vexnum; j++)
if (ga.cost[numb][j]!=0 && ga.cost[numb][j]!=MAXINT)
d++;
return (d);
}
2. 编写一个算法,求出邻接矩阵表示的有向图中序号为numb的顶点的度数。
int degree2(Graph & ga, int numb)
2. int degree2(Graph & ga, int numb)
//根据有向图的邻接矩阵求出序号为numb的顶点的度数
{ int i,j,d=0;
//求出顶点numb的出度
for(j=0; j<ga.vexnum; j++)
if(ga.cost[numb][j]!=0 && ga.cost[numb][j]!=MAXINT)
d++;
//求出顶点numb的入度
for(i=0; i<ga.vexnum; i++)
if(ga.cost[i][numb]!=0 && ga.cost[i][numb]!=MAXINT)
d++;
//返回顶点numb的度
return (d);
}
3. 编写一个算法,求出邻接表表示的无向图中序号为numb的顶点的度数。
int degree3(GraphL & gl, int numb)
3. int degree3(GraphL & gl, int numb)
//根据无向图的邻接表求出序号为numb的顶点的度数
{ int d=0;
vexnode * p=gl.adjlist[numb];
while(p!=NULL)
{ d++;
p=p->next;
}
return (d);
}
4. 编写一个算法,求出邻接表表示的有向图中序号为numb的顶点的度数。
int degree4(GraphL & gl, int numb)
4. int degree4(GraphL & gl, int numb)
//根据有向图的邻接表求出序号为numb的顶点的度数
{ int d=0, i;
vexnode * p=gl.adjlist[numb];
while (p!=NULL)
{ d++;
p=p->next;
} //求出顶点numb的出度
for(i=0; i<gl.vexnum; i++)
{ p=gl.adjlist[i];
while(p!=NULL)
{ if(p->vertex= =numb) d++;
p=p->next;
}
}//求出顶点numb的入度
return (d); //返回顶点numb的度数
}