1、第七章 图 习题答案基础知识:7.1 在图7.23所示的各无向图中:(1)找出所有的简单环。(2)哪些图是连通图?对非连通图给出其连通分量。(3)哪些图是自由树(或森林)?答:(1)所有的简单环:(同一个环可以任一顶点作为起点)(a)1231(b)无(c)1231、2342、12341(d)无(2)连通图:(a)、(c)、(d)是连通图,(b)不是连通图,因为从1到2没有路径。具体连通分量为:(3)自由树(森林):自由树是指没有确定根的树,无回路的连通图称为自由树:(a)不是自由树,因为有回路。(b)是自由森林,其两个连通分量为两棵自由树。(c)不是自由树。(d)是自由树。 7.2 在图7.2
2、4(下图)所示的有向图中: (1) 该图是强连通的吗? 若不是,则给出其强连通分量。(2) 请给出所有的简单路径及有向环。(3) 请给出每个顶点的度,入度和出度。(4) 请给出其邻接表、邻接矩阵及逆邻接表。答:(1)该图是强连通的,所谓强连通是指有向图中任意顶点都存在到其他各顶点的路径。(2)简单路径是指在一条路径上只有起点和终点可以相同的路径:有v1v2、v2v3、v3v1、v1v4、v4v3、v1v2v3、v2v3v1、v3v1v2、v1v4v3、v4v3v1、v3v1v4、另包括所有有向环,有向环如下:v1v2v3v1、v1v4v3v1(这两个有向环可以任一顶点作为起点和终点)(3)每个
3、顶点的度、入度和出度:D(v1)=3 ID(v1)=1 OD(v1)=2D(v2)=2 ID(v2)=1 OD(v2)=1D(v3)=3 ID(v3)=2 OD(v3)=1D(v4)=2 ID(v4)=1 OD(v4)=1(4)邻接表:(注意边表中邻接点域的值是顶点的序号,这里顶点的序号是顶点的下标值-1) vertex firstedge next 0v1 1 3 1v2 2 2v3 0 3v4 2 逆邻接表: 0v1 2 1v2 0 2v3 1 3 3v4 0 邻接矩阵: 0 1 0 1 0 0 1 0 1 0 0 0 0 0 1 0 7.3 假设图的顶点是A,B.,请根据下述的邻接矩阵画
4、出相应的无向图或有向图。 | 0 1 1 0 0 | | 0 1 1 1 | | 0 0 0 1 0 | | 1 0 1 1 | | 0 0 0 1 0 | | 1 1 0 1 | | 1 0 0 0 1 | | 1 1 1 0 | | 0 1 0 1 0 | (a) (b)答: 7.4 假设一棵完全二叉树包括A,B,C.等七个结点,写出其邻接表和邻接矩阵。解: 邻接表如下: 0A 1 2 1B 0 3 4 2C 0 5 6 3D 1 4E 1 5F 2 6G 2 邻接矩阵如下: 0 1 1 0 0 0 0 1 0 0 1 1 0 0 1 0 0 0 0 1 1 0 1 0 0 0 0 0 0
5、 1 0 0 0 0 0 0 0 1 0 0 0 0 0 0 1 0 0 0 07.5 对n个顶点的无向图和有向图,采用邻接矩阵和邻接表表示时,如何判别下列有关问题? (1) 图中有多少条边? (2)任意两个顶点i和j是否有边相连? (3) 任意一个顶点的度是多少?答: 对于n个顶点的无向图和有向图,用邻接矩阵表示时: (1)设m为矩阵中非零元素的个数无向图的边数=m/2有向图的边数=m(2)无论是有向图还是无向图,在矩阵中第i行,第j列的元素若为非零值,则该两顶点有边相连。(3)对于无向图,任一顶点i的度为第i行中非零元素的个数。对于有向图,任一顶点i的入度为第i列中非零元素的个数,出度为第
6、i行中非零元素的个数,度为入度出度之和。当用邻接表表示时:(1)对于无向图,图中的边数=边表中结点总数的一半。对于有向图,图中的边数=边表中结点总数。(2)对于无向图,任意两顶点间是否有边相连,可看其中一个顶点的邻接表,若表中的adjvex域有另一顶点位置的结点,则表示有边相连。对于有向图,则表示有出边相连。(3)对于无向图,任意一个顶点的度则由该顶点的边表中结点的个数来决定。对于有向图,任意一个顶点的出度由该顶点的边表中结点的个数来决定,入度则需遍历各顶点的边表。(用逆邻接表可容易地得到其入度。)7.6 n个顶点的连通图至少有几条边?强连通图呢?答:n个顶点的连通图至少有n-1条边,强连通图
7、至少有2(n-1)条边。7.7 DFS和BFS遍历各采用什么样的数据结构来暂存顶点?当要求连通图的生成树的高度最小,应采用何种遍历?答:DFS遍历采用栈来暂存顶点。BFS采用队列来暂存顶点。当要求连通图的生成树的高度最小时,应采用BFS遍历。7.8 画出以顶点v1为初始源点遍历图7.25(下图)所示的有向图所得到的DFS 和BFS生成森林。答:7.9 按顺序输入顶点对:(1,2),(1,6),(2,6),(1,4),(6,4),(1,3),(3,4),(6,5),(4,5),(1,5),(3,5),根据第7.2.2节中算法CreatALGraph画出相应的邻接表。并写出在该邻接表上,从顶点4开
8、始搜索所得的DFS和BFS序列,及DFS和BFS生成树。答:相应的邻接表如下: 11 5 3 4 6 2 22 6 1 33 5 4 1 44 5 36 1 55 3 1 4 6 66 5 4 2 1 根据上面的邻接表画出的图见下图: 从顶点4开始搜索所得的DFS序列是:453162 从顶点4开始搜索所得的BFS序列是:453612 相应的生成树见上图。7.10 什么样的图其最小生成树是唯一的?用PRIM 和Kruskal求最小生成树的时间各为多少?它们分别适合于哪类图?答:当候选轻边集中的轻边数始终等于1条时,其最小生成树是唯一的。用Prim和Kruskal求最小生成树的时间复杂度分别为O(
9、n2)和O(elge),前者适合于稠密图,后者适合于稀疏图.7.11 对图7.26(下图)所示的连通图,请分别用Prim和Kruskal算法构造其最小生成树。答:7.12 对图7.27(下图)所示的有向图,试利用Dijkstra算法求出从源点1到其它各顶点的最短路径,并写出执行算法过程中扩充红点集的每次循环状态(见表7.2). 答:循环状态表如下:循环 红点集 K D1 D2 D3 D4 D5 D6 P1 P2 P3 P4 P5 P6初始化 1 - 0 20 15 -1 1 1 -1 -1 -1 1 1,3 3 0 19 15 25 -1 3 1 -1 -1 3 2 1,3,2 2 0 19
10、15 29 25 -1 3 1 -1 2 3 3 1,3,2,6 6 0 19 15 29 29 25 -1 3 1 6 2 3 4 1,3,2,6,4 4 0 19 15 29 29 25 -1 3 1 6 2 3 5 1,3,2,6,4,5 5 0 19 15 29 29 25 -1 3 1 6 2 3 6 同上 - 同上 同上从源点1到各点的路径如下所示: 1到2:1321到3:131到4:13641到5:13251到6:136整个执行算法过程中的扩充红点集的每次循环状态见上表。7.13 试写出图7.28(下图)所示有向图的所有拓扑序列,并指出就用7.6节给出的NonPreFirstTo
11、pSort算法求得的是哪个序列,设邻接表的边表结点中的邻接点序号是递增有序的。 答: 上图中所有拓扑序列如下: v0v1v5v2v3v6v4 * v0v1v5v2v6v3v4 v0v1v5v6v2v3v4 v1v0v5v6v2v3v4 v1v0v5v2v3v6v4 v1v0v5v2v6v3v4 v1v5v0v2v3v6v4 v1v5v0v2v6v3v4 v1v5v0v6v2v3v4 v5v1v0v2v3v6v4 v5v1v0v2v6v3v4 v5v1v0v6v2v3v4 v5v0v1v2v3v6v4 v5v0v1v2v6v3v4 v5v0v1v6v2v3v4 v0v5v6v1v2v3v4 v1
12、v5v6v0v2v3v4 v5v6v1v0v2v3v4 v5v6v0v1v2v3v4 v5v0v6v1v2v3v4 v5v1v6v0v2v3v4 用NonPreFirstTopSort算法求得的是v0v1v5v2v3v6v4也就是上面带*号的那个。7.14 什么样的DAG的拓扑序列是唯一的?答: 确定了排序的源点,DAG图中无前趋顶点只有一个且从该点到终点只有一条路径时,它的拓扑序列才是唯一的。7.15 请以V0为源点,给出用DFS搜索图7.28(下图)得到的逆拓扑序列。 答: 逆拓扑序列是:V4 V2 V1 V0 V1 V6 V5算法设计:7.16 试在无向图的邻接矩阵和邻接链表上实现如下算
13、法:(1)往图中插入一个顶点(2)往图中插入一条边(3)删去图中某顶点(4)删去图中某条边解:(一)用邻接矩阵表示#define MaxVertexNum l00 /最大顶点数,应由用户定义typedef char VertexType; /顶点类型应由用户定义typedef int EdgeType; /边上的权值类型应由用户定义typedef structVextexType vexsMaxVertexNum /顶点表EdeType edgesMaxVertexNumMaxVertexNum;/邻接矩阵,可看作边表int n,e; /图中当前的顶点数和边数MGragh;(1)往图中插入一个
14、顶点void AddVertexMGraph(MGraph *G,VertexType x)/往无向图的邻接矩阵中插入顶点if (G-n=MaxVertexNum)Error(顶点数太多);G-vexsG-n=x;/将新顶点输入顶点表G-n+;/顶点数加1(2)往图中插入一条边void AddedgeMGraph(MGraph *G,VertexType x,VertexType y)/往无向图的邻接矩阵中插入边(x,y)int i,j,k;i=-1;j=-1;for(k=0;kn;k+)/查找X,Y的编号 if (g-vexsk=x) i=k;if (g-vexsk=y) j=k;if (i
15、=-1|j=-1) Error(结点不存在);else /插入边(x,y)g-vexij=1;g-vexji=1;G-e+;/边数加1(3)删去图中某顶点void DeleteVertexMGraph(MGraph *G,VertexType x)/无向图的邻接矩阵中删除顶点xint i,k,j;i=-1;for(k=0;kn;k+)/查找X的编号if (g-vexsk=x) i=k;if (i=-1) Error(结点不存在);else /删除顶点以及边k=0;/求出与x结点相关联的边数kfor (j=0;jn;j+)if (g-vexsij=0) k+;G-e=G-e-k;/设置新的边数f
16、or (k=i+1;kn;k+)/在邻接矩阵中删除第i行for(j=0;jn;j+)g-vexsk-1j=g-vexskj;for (k=i+1;kn;k+)/在邻接矩阵中删除第i列for(j=0;jn;j+)g-vexsjk-1=g-vexsjk;G-n-;/总结点数-1(4)删去图中某条边void DeleteedgeMGraph(MGraph *G,VertexType x,VertexType y)/无向图的邻接矩阵中删除边(x,y)int i,j,k;i=-1;j=-1;for(k=0;kn;k+)/查找X,Y的编号 if (g-vexsk=x) i=k;if (g-vexsk=y)
17、 j=k;if (i=-1|j=-1) Error(结点不存在);else if (g-vexsij!=1)/删除边(x,y)g-vexij=0;g-vexji=0;G-e-;/边数加1(二)用邻接表表示typedef struct node/边表结点int adjvex; /邻接点域struct node *next; /链域/若要表示边上的权,则应增加一个数据域EdgeNode;typedef struct vnode /顶点表结点 VertexType vertex; /顶点域EdgeNode *firstedge;/边表头指针 VertexNode;typedef VertexNode
18、 AdjListMaxVertexNum;/AdjList是邻接表类型typedef struct AdjList adjlist;/邻接表 int n,e; 图中当前顶点数和边数ALGraph; /对于简单的应用,无须定义此类型,可直接使用AdjList类型。(1)往图中插入一个顶点void AddVertexALGraPh(ALGrahp *G,VertexType x)/往无向图的邻接表表示中插入一个顶点if (G-n=MaxVertexNum)Error(顶点数太多);G-adjlistG-n.vertex=x;/将新顶点输入顶点表G-adjlistG-n.firstedge=NULL
19、;/边表置为空表G-n+;/顶点数加1(2)往图中插入一条边void AddedgeALGraPh(ALGrahp *G,VertexType x,VertexType y)/往无向图的邻接表中插入边(x,y)int i,j,k;EdgeNode *s;i=-1;j=-1;for(k=0;kn;k+)/查找X,Y的编号 if (G-adjlistk.vertex=x) i=k;if (G-adjlistk.vertex=y) j=k;if (i=-1|j=-1) Error(结点不存在);else s=G-adjlisti.firstedge;while (s)&(s-adjvex!=j)/查
20、看邻接表中有无(x,y)s=s-next;if (!s)/当邻接表中无边(x,y),插入边(x,y)s=(EdgeNode *)malloc(sizeof(EdgeNode); /生成边表结点s-adjvex=j; /邻接点序号为js-next=G-adjlisti.firstedge;G-adjlisti.firstedge=s;/将新结点*s插入顶点x的边表头部s=(EdgeNode *)malloc(sizeof(EdgeNode);s-adjvex=i; /邻接点序号为is-next=G-adjlistj.firstedge;G-adjlistj.firstedge=s; /将新结点*
21、s插入顶点x的边表头部G-e+;/边数加1(3)删去图中某顶点void DeleteVertexALGraPh(ALGrahp *G,VertexType x)/无向图的邻接表中删除顶点xint i,k,j;EdgeNode *s,*p,*q;i=-1;for(k=0;kn;k+)/查找X的编号if (G-adjlistk.vertex=x) i=k;if (i=-1) Error(结点不存在);else /删除与x相关联的边s=G-adjlisti.firstedge;while (s)p=G-adjlists-adjvex.firstedge;/删除与i相关联的其他结点边表中表结点if (
22、p)&(p-adjvex=i)/是第一个边表结点,修改头指针G-adjlists-adjvex.firstedge=p-next;free(p);else/不是第一个 边表结点,查找并删除while (p)&(p-next)&(p-next-adjvex=i)p=p-next;q=p-next;p-next=q-next;free(q);q=s;s=s-next;free(q);/在i结点的边表中删除表结点G-e-;/调整顶点表for (j=i;jn-1;j+)G-adjlistj.firstedge=G-adjlistj+1.firstedge;G-adjlistj.vertex=G-adj
23、listj+1.vertex;G-n-;(4)删去图中某条边void DeleteedgeALGraPh(ALGraph *G,VertexType x,VertexType y)/往无向图的邻接表中删除边(x,y)int i,j,k;EdgeNode *s,*p;i=-1;j=-1;for(k=0;kn;k+)/查找X,Y的编号 if (G-adjlistk.vertex=x) i=k;if (G-adjlistk.vertex=y) j=k;if (i=-1|j=-1) Error(结点不存在);else s=G-adjlisti.firstedge;/在i的边表中删除值为j的边表结点if
24、 (s)&(s-adjvex=j)G-adjlisti.firstedge=s-next;free(s);elsewhile (s)&(s-next)&(s-next-adjvex!=j)s=s-next;if (!s-next) Error(无此边);elsep=s-next;s=p-next;free(p);s=G-adjlistj.firstedge;/在j的边表中删除值为i的边表结点if (s)&(s-adjvex=i)G-adjlistj.firstedge=s-next;free(s);elsewhile (s)&(s-next)&(s-next-adjvex!=j)s=s-nex
25、t;p=s-next;s=p-next;free(p);G-e-;7.17 下面的伪代码是一个广度优先搜索算法,试以图7.29(下图)中的v0为源点执行该算法,请回答下述问题:(1)对图中顶点vn+1,它需入队多少次?它被重复访问多少次?(2)若要避免重复访问同一个顶点的错误,应如何修改此算法?void BFS(ALGraph *G,int k)/以下省略局部变量的说明,visited各分量初值为假InitQueue(&Q);/置空队列EnQueue(&Q,k);/k入队while(!QueueEmpty(&Q)i=DeQueue(&Q);/vi出队visitedi=TRUE;/置访问标记pr
26、intf(%c,G-adjlisti.vertex;/访问vifor(p=G-adjlisti.firstedge;p;p=p-next)/依次搜索vi的邻接点vj(不妨设p-adjvex=j)if(!visitedp-adjvex)/若vj没有访问过EnQueue(&Q,p-adjvex);/vj入队/endofwhile/BFS答:(1)对图中顶点vn+1,它需入队n次,它被重复访问n次。(2)若要避免重复访问同一个顶点的错误,应作如下修改:void BFS(ALGraph *G,int k)/以下省略局部变量的说明,visited各分量初值为假InitQueue(&Q);/置空队列EnQ
27、ueue(&Q,k);/k入队visitedi=TRUE;/置访问标记printf(%c,G-adjlisti.vertex;/访问viwhile(!QueueEmpty(&Q)i=DeQueue(&Q);/vi出队for(p=G-adjlisti.firstedge;p;p=p-next)/依次搜索并访问vi的未访问邻接点vj(不妨设p-adjvex=j)if(!visitedp-adjvex)/若vj没有访问过printf(%c,G-adjlisti.vertex;/访问vjEnQueue(&Q,p-adjvex);/vj入队/endofwhile)/BFS7.18 试以邻接表和邻接矩阵为
28、存储结构,分别写出基于DFS和BFS遍历的算法来判别顶点vi和vj(ij)之间是否有路径。答:(1)基于采用邻接矩阵的DFSint pathDFSM(MGraph *G,int i,int j) /以邻接矩阵为存储结构,判断vi和vj之间是否有路径,若有返回1,否则返回0int k;visitedi=TRUE;for(k=0;kn;k+) /依次搜索vi的邻接点if(G-edgesik=1&!visitedk)if (k=j) return 1;/有路径相通else return(pathDFSM(G,k,j);return 0;/无路径相通/DFSM(2)基于采用邻接表的DFSint PAT
29、HDFS(ALGraph *G,int i,int j)/以邻接表为存储结构,判断vi和vj之间是否有路径,若有返回1,否则返回0EdgeNode *p;visitedi=TRUE; /标记vi已访问p=G-adjlisti.firstedge; /取vi边表的头指针while(p)/依次搜索vi的邻接点vk,这里k=p-adjvexif (!visitedp-adjvex)/若vk尚未被访问if (p-adjvex=j)return 1;else ruturn PATHDFS(g,p-adjvex,j);/则以Vk为出发点向纵深搜索p=p-next; /找vi的下一邻接点return 0;/
30、PATHDFS(3)基于邻接矩阵的BFS算法int pathBFSM(MGraph *G,int i,int j)/以邻接矩阵为存储结构,判断vi和vj之间是否有路径,若有返回1,否则返回0int k;CirQueue Q;initQueue(&Q);visitedi=TRUE;EnQueue(&Q,i);while(!QueueEmpty(&Q)i=DeQueue(&Q); /vi出队for(k=0;kn;k+)/依次搜索vi的邻接点vkif(G-edgesik=1&!visitedk)/vk未访问if (k=j) return 1;visitedk=TRUE;EnQueue(&Q,k);/
31、访问过的vk人队/endwhilereturn 0;/pathBFSM(4)基于邻接表为存储结构的BFS算法int BFS(ALGraph *G,int i,int j)/以邻接表为存储结构,判断vi和vj之间是否有路径,若有返回1,否则返回0int i;CirQueue Q; /须将队列定义中DataType改为intEdgeNode *p;InitQueue(&Q);/队列初始化visitedi=TRUE;EnQueue(&Q,i);/vi已访问,将其人队。(实际上是将其序号人队)while(!QueueEmpty(&Q)/队非空则执行i=DeQueue(&Q); /相当于vi出队p=G-
32、adjlisti.firstedge; /取vi的边表头指针 while(p)/依次搜索vi的邻接点vk(令p-adjvex=k)if(!visitedp-adjvex) /若vk未访问过if (p-adjvex=j)return 1;elsevisitedP-adjvex=TRUE;EnQueue(&Q,p-adjvex);/访问过的vk人队 p=p-next;/找vi的下一邻接点 /endwhile /endwhile return 0; /end of pathBFS7.19 试分别写出求DFS和BFS生成树(或生成森林)的算法,要求打印出所有的树边。答: (1)求DFS生成树typedef enumFALSE,TRUEBoolean;/FALSE为0,TRUE为1Boolean visitedMaxVertexNum; /访问标志向量是全局量void