8.1 知识点分析
1.图的定义
图(Graph)是由非空的顶点(Vertices)集合和一个描述顶点之间关系——边(Edges)的有限集合组成的一种数据结构。可以用二元组定义为:
G=(V,E)
其中,G表示一个图,V是图G中顶点的集合,E是图G中边的集合。
2.图的相关术语
(1)无向图
在一个图中,如果每条边都没有方向,则称该图为无向图。
(2)有向图
在一个图中,如果每条边都有方向,则称该图为有向图。
(3)无向完全图
在一个无向图中,如果任意两顶点都有一条直接边相连接,则称该图为无向完全图。在一个含有n个顶点的无向完全图中,有n(n-1)/2条边。
(4)有向完全图
在一个有向图中,如果任意两顶点之间都有方向互为相反的两条弧相连接,则称该图为有向完全图。在一个含有n个顶点的有向完全图中,有n (n-1) 条弧。
(5)顶点的度
在无向图中,一个顶点拥有的边数,称为该顶点的度。记为TD(v)。
在有向图中,一个顶点拥有的弧头的数目,称为该顶点的入度,记为ID(v);一个顶点拥有的弧尾的数目,称为该顶点的出度,记为OD(v);一个顶点度等于顶点的入度+出度,即:TD(v)=ID(v)+OD(v)。
(6)权
图的边或弧有时具有与它有关的数据信息,这个数据信息就称为权(Weight)。
(7)网
边(或弧)上带权的图称为网(Network)。
(8)路径、路径长度
顶点vi到顶点vj之间的路径(Path)是指顶点序列vi,vi1,vi2, …, vim,vj.。其中,(vi,vi1),(vi1,vi2),…,(vim,.vj)分别为图中的边。
路径上边的数目称为路径长度。
(9)回路、简单路径
在一条路径中,如果其起始点和终止点是同一顶点,则称其为回路或者环(Cycle)。如果一条路径上所有顶点除起始点和终止点外彼此都是不同的,则称该路径为简单路径。
(10)子图
对于图G=(V,E),G’=(V’,E’),若存在V’是V的子集 ,E’是E的子集 ,则称图G’是G的一个子图。
(11)连通图、连通分量
在无向图中,如果从一个顶点vi到另一个顶点vj(i≠j)有路径,则称顶点vi和vj是连通的。任意两顶点都是连通的图称为连通图。
无向图的极大连通子图称为连通分量。
(12)强连通图、强连通分量
对于有向图来说,若图中任意一对顶点vi 和vj(i≠j)均有从一个顶点vi到另一个顶点vj有路径,也有从vj到vi的路径,则称该有向图是强连通图。
有向图的极大强连通子图称为强连通分量
(13)生成树
连通图G的一个子图如果是一棵包含G的所有顶点的树,则该子图称为G的生成树(Spanning Tree)。
3.图的存储表示
(1)邻接矩阵
邻接矩阵是表示顶点之间相邻关系的矩阵。
(2)邻接表
邻接表是图的一种顺序存储与链式存储结合的存储方法。
4.图的遍历
图的遍历是指从图的某一顶点出发,对图中的所有顶点访问,且仅访问一次的方法。常用的有深度优先搜索和广度优先搜索两种方法。
5.最小生成树
连通图的一次遍历所经过的边的集合及图中所有顶点的集合就构成了该图的一棵生成树,对连通图的不同遍历,就可能得到不同的生成树,生成树中权值之和为最小的生成树,称为最小生成树。
6.最短路径
在网中,两个顶点之间所有路径中,边的权值之和最短的那一条路径。这条路径就称为两点之间的最短路径。
8.2 典型习题分析
【例1】设有两个无向图G=(V,E),G=(V′,E′),如果G′是G生成子树,则下述叙述不正确的是( )。
A.G′是G的子图 B.G′是G的连通分量
C.G′是G的无环子图 D.G′是G极小连通子图,且V′=V
分析:如果G′是G生成子树,显然G是G的子图、G′是G的无环子图、G′是G的连通分量和G′是G极小连通子图但是V′≠V,故D不正确,答案为D。
【例2】若一个有向图具有拓扑序列,则它的矩阵必为( )。
A.对称矩阵 B.三角矩阵 C.一般矩阵 D.B或C
分析:拓扑排序存在当仅当有向无环图,若一个有向图的邻接矩阵是三角形矩阵,则该图一定无环;但一个无环图的有向图的邻接矩阵未必是三角形。因此,应该选择D。
【例3】用邻接矩阵表示图时,若图中有1000个顶点,1000条边,则形成的邻接矩阵有多少矩阵元素?有多少非零元素?是否稀疏矩阵?
解:一个图中有1000个顶点,其邻接矩阵中的矩阵元素有10002 = 1000000个。它有1000个非零元素(对于有向图)或2000个非零元素(对于无向图),因此是稀疏矩阵。
【例4】用邻接矩阵表示图时,矩阵元素的个数与顶点个数是否相关?与边的条数是否相关?
答:用邻接矩阵表示图,矩阵元素的个数是顶点个数的平方,与边的条数无关。矩阵中非零元素的个数与边的条数有关。
【例5】Prim算法最小生成树的时间复杂度为_____________,因此它适合于___________图;Kruskal算法最小生成树的时间复杂度为___________,因此适合于__________图,且图应该用__________作为存储结构。
分析:因为Prim算法的时间复杂度只与顶点数n有关,故对稀疏图不太适合,而Kruskal算法只与边数e有关,故对稀疏图较适合,且用邻接表作为存储结构为宜。依次填写为O(n2)、稠密图、O(eloge)、稀疏图、邻接矩阵。
【例6】设有一有向图为G=(V,E)。其中,V={ v1, v2, v3, v4, v5},E={ 分析:作该题的关键是弄清楚以下两点 (1)边集E中 (2)强连通图是任意两顶点间都存在路径的有向图。 答:该有向图是强连通图,表示如图8-1所示。 图8-1 强连通有向图 【例7】画出有向图8-2所示有向图的邻接矩阵、邻接表、逆邻接表、十字链表。写出邻接表表示的图从顶点A出发的深度优先遍历序列和广度优先遍历序列。 图8-2 有向图 答:图8-2的邻接矩阵、邻接表、逆邻接表、十字链表,如图8-3、图8-4、图8-5、图8-6所示。 图8-3 邻接矩阵 图8-4邻接表 图8-5逆邻接表 图8-6 十字链表 深度优先遍历序列为:ABCFED;广度优先遍历序列为ABDCEF。 【例8】已知无向图G的邻接表如图8-7所示,请写出其从顶点V2开始的深度优先搜索的序列。 图8-7 邻接表 分析:图搜索可从图中某个顶点发V出发,首先访问此顶点,然后任选一个V的未被访问的邻接点w出发,继续进行深度优先搜索,直到图中所有和v路径相通的顶点都被访问到;若此时图中还有顶点未被访问到,则另选一个未被访问的顶点作为起始点,重复上面的做法,直至图中所有的顶点都被访问。 根据图从顶点V2开始的深度优先搜索的序列是V2,V2的邻接点有四个,按深度有先法则和给定邻接表,只能访问V5。再从V5出发,第一个邻接点V2已经访问过,只能访问V3。再从V3出发,同样第一个邻接点V2已经访问,只能访问V1。再从V5出发,第一个邻接点V3和第二邻接点V2已经访问过,退回到V3。在V3,V1下一个V4没有访问过,即访问,这样五个顶点都被访问,他们的顺序是V2,V5,V3,V1,V4。 这里需要说明的是如果没有邻接表,从顶点V2出发对这个无向图深度优先搜索的访问次序不唯一,但是有邻接表是唯一。 答:深度优先搜索的访问次序V2,V5,V3,V1,V4。 【例9】设计一个算法,删除无向图的邻接矩阵中给定顶点。 分析:要在邻接矩阵中删除某顶点i主要操作有以下三步: (1)图的边数要减去与顶点i相关联的边的数目; (2)在邻接矩阵中删除第i行与i列,即把第i+1行到第n行依次前移,第i+1列到第n列依次前移。 (3)图中顶点的个数-1。 算法如下: void Delvi(MGraph *G,int i) // 在图 G中删除顶点i { int num,j,k; if(i<1 || i>G->vexnum) {printf("error"); exit(0);} else { num=0; for(j=1;j<=G->vexnum;j++) // 计算与i相关的边数 { if(G->arcs[i][j]) num++; if(G->arcs[j][i]) num++; } G->arcnum-=num; // 减去与i相关的边数 for(j=i+1;j<=G->vexnum;j++) for(k=1;k<=G->vexnum;k++) G->arcs[j-1][k]=G->arcs[j][k]; // 从i+1行到最后一行元素都 向前移动一行 for(j=i+1;j<=G->vexnum;j++) // 从i+1列到最后一列元素都 向前移动一列 for(k=1;k<=G->vexnum-1;k++) G->arcs[k][j-1]=G->arcs[k][j]; G->vexnum--; // 顶点数减去1 } } 【例10】已知某有向图用邻接表表示,设计一个算法,求出给定两顶点间的简单路径。 分析:因为在遍历的过程中每个顶点仅被访问一次,所以从顶点u到顶点v遍历的过程中走过的路径就是一条简单路径。我们只需在遍历算法中稍作修改,就可实现该算法。为了记录路径中访问过的顶点,只需设一数组存储走过的顶点即可。 求入度的思想:计算出邻接表中结点i的结点数即可。 int visited[MAX_VERTEX_NUM]; int found; void DFSpath(ALGraph *G,int u,int v) { int i; for(i=1;i<=G->vexnum;i++) visited[i]=0; found=0; DFS(G,u,v); } int path[MAX_VERTEX_NUM]; // MAX_VERTEX_NUM为结点个数 void DFS(ALGraph *G,int u,int v) { // 用深度优先遍历算法实现简单路径的求解 ArcPtr p; if(found) return; if(G->ag[u].firstarc==NULL) { printf(“no path\\n”); return; } visited[u]=1; for(p=G->ag[u].firstarc;p;p=p->nextarc) // 取第一个边 { if (v==p->adjvex) // 路径找到,输出 {path[u]=v; found=1; Print(u,v); return;} else if(!visited[p->adjvex]) // 取下一条边 {path[u]=p->adjvex; DFS(G,p->adjvex,v);} // 递归 } } // DFS void Print(int u,int v) // 指印路径 { int m; printf("%d->",u); for(m=path[u];m!=v;m=path[m]) printf("%d->",m); printf("%d\\n",v); } 8.3 单元练习8解答 一.判断题答案 (1)邻接表 (2)深度优先搜 (3)2n (4)弧 (5)顶点 (6)出度 (7)O(n2) (8)O(n+e) (9)邻接表 (10)邻接矩 (11)有向 (12)n(n-1)/2 (13)出度 (14)入度 (15)n-1 条边。 (16)n+e 个结点。 (17)遍历 (18)对称 (19)权 (20)Prim 三.选择题答案 (1)邻接矩阵 1 2 3 4 5 邻接表 从顶点0出发的深度优先搜索遍历的结点序列:0 1 2 3 4 5(答案不唯一) 从顶点0出发的广度优先搜索遍历的结点序列:0 1 2 4 5 3(答案不唯一) (3)答: 深度优先搜索: a b d c e (答案不唯一) 广度优先搜索: a b e d c (答案不唯一) (4)答: 最小生成树: 8 11 8 10 3 4 3 4 13 7 7 (5) 答: ① ② 完全无向图应具有的边数为:n*(n-1)1/2=4*(4-1)/2=6,所以还要增加2条边(如下图)。 (6)答: ① ② (7)答: ①邻接矩阵为: ②起点为a,可以直接由原始图画出最小生成树 (8)答: ①邻接矩阵 ②最小生成树 五.程序题填空题答案 (1) ERROR (2) ERROR (3) 0 (4) -- 或=G.arcnum-1 (5) OK 六.算法题答案 (1)分析:本题思想是逐个扫描邻接矩阵的各个元素,若第i行第j列的元素为1,则相应的邻接表的第i个单链表上增加一个j结点。 【函数代码】 void trans(int edges[n][n],Adjlist adj) { int i,j; edgenode *p; for (i=0;i adj[i].link=NULL; } for (i=0;i { p=(edgenode *) malloc (sizeof(edgenode)); p->adjvex=j; p->next=adj[i].link; adj[i].link=p; } } } (2) ① 分析:计算出邻接表中第i个单链表的结点数即可。 【函数代码】 int outdegree(adjlist adj,int v) { int degree=0; edgenode *p; p=adj[v].link; while (p!=NULL) { degree++; p=p->next; } return degree; } void printout(adjlist adj,int n) { int i,degree; printf("The Outdegree are:\\n"); for(i=0;i printf("(%d,%d)",i,degree); } } ② 分析:计算出邻接表中结点i的结点数即可。 【函数代码】 int indegree(adjlist adj,int n,int v) { int i,j,degree=0; edgenode *p; for (i=0;i while (p!=NULL) { if (p->adjvex==v) degree++; p=p->next; } } return degree; } void printin(adjlist adj,int n) { int i,degree; printf("The Indegree are:\\n"); for (i=0;i printf("(%d,%d)",i,degree); } } ③ 求最大度的算法 【函数代码】 void maxoutdegree(adjlist adj,int n) { int maxdegree=0,maxv=0,degree,i; for (i=0;i if (degree>maxdegree) { maxdegree=degree; maxv=i; } } printf("maxoutdegree %d,maxvertex=%d",maxdegree,maxv); } (3)求度为0的顶点数的算法 int outzero(adjlist adj,int n) { int num=0,i; for (i=0;i num++; } return num; }下载本文
二.填空题答案题目 1 2 3 4 5 6 7 8 9 10 答案 √ × × √ × × √ √ × √
四.应用题答案题目 1 2 3 4 5 6 7 8 9 10 答案 C B B C C A D A B B 题目 11 12 13 14 15 16 17 18 19 20 答案 C A C B A D A A A C
(2) 答:1 2 3 5 ∧ 2 4 ∧ 3 5 ∧ 4 1 ∧ 5 4 ∧
③顶点 1 2 3 4 5 6 入度 3 2 1 1 2 2 出度 0 2 2 3 1 3