图学习要点

概述

图是一种非常复杂的非线性结构,并且具有极强的表达能力,现实世界中的许多问题都可以抽象为图结构。本章是整个数据结构课程的难点和重点,本章知识点的组织结构如下图所示:
在这里插入图片描述

重点/难点/要点

本章的重点是:

  1. 图的基本术语;
  2. 图的各种存储表示:
  3. 图的两种遍历的思想及算法:
  4. 图的各种应用。

本章的难点是:

  1. 运用图的遍历算法解决图的其他相关问题;
  2. 最小生成树算法;
  3. 最短路径算法;
  4. 拓扑排序算法:
  5. 关键路径算法。

图学习要点
对于本章的学习要抓住一条明线:图的逻辑结构→图的存储结构→图的应用举例。对于图的逻辑结构,要从图的定义出发,抓住要点,在与线性表的定义和树的定义比较的基础上,深刻理解图结构的逻辑特征,通过具体实例理解图的基本术语,在与树的遍历进行比较的基础上,从逻辑上掌握图的遍历操作,最后给出图的抽象数据类型定义。对于图的存储结构,以如何表示图中顶点之间的逻辑关系为出发点,掌握图的不同存储结构以及它们之间的关系,并学会在实际问题中修改存储结构。在理解图的存储结构和遍历操作的基础上,基于邻接矩阵和邻接表存储结构实现图的遍历操作。
图有很多重要应用,这些重要应用构成了本章的难点,对这些重要应用的学习,首先要把握其基本思想,其次,掌握算法的执行过程和顶层伪代码描述,再次,分析算法采用的存储结构和引入的辅助数据结构,最后才能掌握具体的算法。

知识点整理

  1. 图是由顶点的有穷非空集合和顶点之间边的集合组成。如果图的任意两个顶点之间的边都是无向边,则称该图为无向图,则称该图为无向图,否则称该图为有向图。
  2. 在无向图中,对于任意顶点 v i v_i vi v j v_j vj,,若存在边 ( v i , v j ) (v_i,v_j) (vi,vj),则称顶点 v i v_i vi v j v_j vj互为邻接点。在有向图中,对于任意顶点 v i v_i vi v j v_j vj,若存在弧 < v i , v j > <v_i,v_j> <vi,vj>,则称顶点 v j v_j vj v i v_i vi的邻接点。
  3. 含有n个顶点的无向完全图共有 n × ( n − 1 ) / 2 n\times(n-1)/2 n×(n1)/2条边;含有n个顶点的有向完全图共有 n × ( n − 1 ) n\times(n-1) n×(n1)条边。
  4. 在无向图中,顶点v的度是指依附于该顶点的边的个数;在有向图中,顶点v的入度是指以该顶点为弧头的弧的个数,顶点v的出度是指以该顶点为弧尾的弧的个数。
  5. 在图中,权通常是指对边赋予的有意义的数值量,边上带权的图称为网或网图。
  6. 在无向图 G = ( V , E ) G=(V,E) G=(V,E)中,顶点 v p v_p vp v q v_q vq之间的路径是一个顶点序列 v p = v i 0 , v i 1 , … , v i m v_p=v_{i0},v_{i1},…,v_{im} vp=vi0,vi1,,vim其中, ( v i j − 1 , v i j ) ∈ E ( 1 ≤ j ≤ m ) (v_{ij-1},v_{ij})\in E(1≤j≤m) (vij1,vij)E(1jm);如果 G G G是有向图,则 < v i j − 1 , v i j > ∈ E ( 1 ≤ j ≤ m ) <v_{ij-1},v_{ij}>\in E(1≤j≤m) <vij1,vij>E(1jm)。路径上边的数目称为路径长度。第一个顶点和最后一个顶点相同的路径称为回路。
  7. 在无向图中,若任意顶点 v i v_i vi v j ( i ≠ j ) v_j(i\neq j) vj(i=j)之间有路径,则称该图是连通图,非连通图的极大连通子图称为连通分量;在有向图中,对任意顶点 v i v_i vi v j ( i ≠ j ) v_j(i\neq j) vj(i=j),若从顶点 v i v_i vi v j v_j vj和从顶点 v j v_j vj v i v_i vi均有路径,则称该有向图是强连通图,非强连通图的极大强连通子图称为强连通分量。
  8. 连通图 G G G的生成树是包含 G G G中全部顶点的一个极小连通子图。图的生成树可以在遍历过程中得到。
  9. 图的遍历通常有深度优先遍历和广度优先遍历两种方式。图的深度优先遍历是以递归方式进行的,需用栈记载遍历路线:图的广度优先遍历是以层次方式进行的,需用队列保存已访问的顶点。
  10. 为了在图的遍历过程中区分顶点是否已被访问,设置一个访问标志数组visited[n],其初值为未被访问标志“0”,如果某个顶点已被访问,则将该顶点的访问标志置为“1”。
  11. 图的存储结构有邻接矩阵、邻接表、十字链表、邻接多重表等,前面两个需要重点掌握。
  12. 图的邻接矩阵存储用一个一维数组存储图中顶点的信息,用一个二维数组存储图中边的信息(邻接矩阵):图的邻接表存储由边表和顶点表组成,图中每个顶点的所有邻接点构成一个边表,所有边表的头指针和存储顶点信息的一维数组构成顶点表。
  13. 最小生成树是无向连通网中代价最小的生成树。最小生成树具有MST性质,Prim算法和Kruskal算法是两个利用MST性质构造最小生成树的经典算法。Prim算法的时间复杂度为 O ( n 2 ) O(n^2) O(n2),适用于求稠密网的最小生成树;Kruskal算法的时间复杂度为 O ( e l o g 2 e ) O(elog_2e) O(elog2e),适用于求稀疏网的最小生成树。
  14. 在网图中,最短路径是指两顶点之间经历的边上权值之和最少的路径。Dijkstra 算法按路径长度递增的次序产生单源点最短路径,时间复杂度为 O ( n 2 ) O(n^2) O(n2)。Floyd算法采用迭代的方式求得每一对顶点之间的最短路径,时间复杂度为 O ( n 3 ) O(n^3) O(n3)
  15. AOV网是用顶点表示活动,用弧表示活动之间的优先关系的有向图,测试AOV网是否存在回路的方法,就是对AOV网进行拓扑排序。
  16. AOE网是用顶点表示事件,用有向边表示活动,边上的权值表示活动的持续时间的有向图,计算完成整个工程的最短工期,找出关键活动的方法是对AOE网求关键路径。

练习

图的逻辑结构
1.带权图指的是(B)。
A.顶点带权的无向图或有向图
B.边上带权的无向图或有向图
C.有回路的无向图或有向图
D.无回路的无向图或有向图
2.在图结构中,逻辑关系表现为邻接,相互邻接的顶点之间具有逻辑关系。(√)
3.最稀疏的图是(B),最稠密的图是(C)。
A.空图 B.零图 C.完全图 D.满图
4.在无向图中,路经可能不唯一;在有向图中,路经是唯一的。(×)
5.一个具有n个顶点的图,其子图可以有 2 n 2^n 2n个。(×)
6.若无向图 G = ( V , E ) G=(V,E) G=(V,E)有两个连通分量 G 1 = ( V 1 , E 1 ) G_1=(V_1,E_1) G1=(V1,E1) G 2 = ( V 2 , E 2 ) G_2=(V_2,E_2) G2=(V2,E2),则有(A)。
A. ∣ V 1 ∣ + ∣ V 2 ∣ = ∣ V ∣ , ∣ E 1 ∣ + ∣ E 2 ∣ = ∣ E ∣ |V_1|+|V_2|=|V|,|E_1|+|E_2|=|E| V1+V2=V,E1+E2=E
B. ∣ V 1 ∣ + ∣ V 2 ∣ < ∣ V ∣ , ∣ E 1 ∣ + ∣ E 2 ∣ < ∣ E ∣ |V_1|+|V_2|<|V|,|E_1|+|E_2|<|E| V1+V2<V,E1+E2<E
C. ∣ V 1 ∣ + ∣ V 2 ∣ = ∣ V ∣ , ∣ E 1 ∣ + ∣ E 2 ∣ < ∣ E ∣ |V_1|+|V_2|=|V|,|E_1|+|E_2|<|E| V1+V2=V,E1+E2<E
D. ∣ V 1 ∣ + ∣ V 2 ∣ < ∣ V ∣ , ∣ E 1 ∣ + ∣ E 2 ∣ = ∣ E ∣ |V_1|+|V_2|<|V|,|E_1|+|E_2|=|E| V1+V2<V,E1+E2=E
7.若有向图 G = ( V , E ) G=(V,E) G=(V,E)有两个连通分量 G 1 = ( V 1 , E 1 ) G_1=(V_1,E_1) G1=(V1,E1) G 2 = ( V 2 , E 2 ) G_2=(V_2,E_2) G2=(V2,E2),则有(C)。
A. ∣ V 1 ∣ + ∣ V 2 ∣ = ∣ V ∣ , ∣ E 1 ∣ + ∣ E 2 ∣ = ∣ E ∣ |V_1|+|V_2|=|V|,|E_1|+|E_2|=|E| V1+V2=V,E1+E2=E
B. ∣ V 1 ∣ + ∣ V 2 ∣ < = ∣ V ∣ , ∣ E 1 ∣ + ∣ E 2 ∣ < = ∣ E ∣ |V_1|+|V_2|<=|V|,|E_1|+|E_2|<=|E| V1+V2<=V,E1+E2<=E
C. ∣ V 1 ∣ + ∣ V 2 ∣ = ∣ V ∣ , ∣ E 1 ∣ + ∣ E 2 ∣ < = ∣ E ∣ |V_1|+|V_2|=|V|,|E_1|+|E_2|<=|E| V1+V2=V,E1+E2<=E
D. ∣ V 1 ∣ + ∣ V 2 ∣ < = ∣ V ∣ , ∣ E 1 ∣ + ∣ E 2 ∣ = ∣ E ∣ |V_1|+|V_2|<=|V|,|E_1|+|E_2|=|E| V1+V2<=V,E1+E2=E
对任意一个图,从某顶点出发进行一次深度优先或广度优先遍历,可访问图的所有顶点。(×)思路:考虑非连通图

对于下图所示无向图,回答下列问题:
在这里插入图片描述

(1)顶点 v 0 v_0 v0的度是(B),顶点 v 1 v_1 v1的度是(C)。
A.1 B.2 C.3 D.4
(2)顶点 v 0 v_0 v0的邻接点是(C)。
A. v 1 v 2 v 3 v_1v_2v_3 v1v2v3 B. v 1 v 2 v_1v_2 v1v2 C. v 1 v 3 v_1v_3 v1v3 D. v 2 v_2 v2
(3)顶点 v 0 v_0 v0 v 1 v_1 v1的最短路径长度是(A)。
A.1 B.2 C.3 D.4
(4)从顶点 v 0 v_0 v0出发的深度优先遍历序列是(D),广度优先遍历序列是(D)
A. v 0 v 2 v 3 v 1 v_0v_2v_3v_1 v0v2v3v1 B. v 0 v 3 v 2 v 1 v_0v_3v_2v_1 v0v3v2v1 C. v 0 v 2 v 1 v 3 v_0v_2v_1v_3 v0v2v1v3 D. v 0 v 1 v 3 v 2 v_0v_1v_3v_2 v0v1v3v2
10.对于下图所示有向网图,回答下列问题:
在这里插入图片描述

(1)顶点 v 0 v_0 v0的入度是(B),出度是(C)。
A.0 B.1C.2 D.3
(2)顶点 v 0 v_0 v0的邻接点是(C)。
A. v 1 v 2 v 3 v_1v_2v_3 v1v2v3 B. v 1 v 2 v_1v_2 v1v2 C. v 2 v 3 v_2v_3 v2v3 D. v 1 v_1 v1
(3)顶点 v 0 v_0 v0 v 3 v_3 v3的最短路径长度是(C)。
A.1 B.2 C.7 D.8
(4)从顶点 v 0 v_0 v0出发的深度优先遍历序列是(B),广度优先遍历序列是(B)。
A. v 0 v 1 v 2 v 3 v_0v_1v_2v_3 v0v1v2v3 B. v 0 v 2 v 3 v 1 v_0v_2v_3v_1 v0v2v3v1 C. v 0 v 3 v 1 v 2 v_0v_3v_1v_2 v0v3v1v2 D. v 0 v 1 v 3 v 2 v_0v_1v_3v_2 v0v1v3v2

图的邻接矩阵存储结构及实现
图的邻接矩阵采用数组方式进行存储,因此属于顺序存储结构。(×)
无向图的邻接矩阵一定是对称的,有向图的邻接矩阵一定是不对称的。(×)
用邻接矩阵存储图,所占用的存储空间大小只与图中顶点个数有关,与图的边数无关。(√)
图采用邻接矩阵存储,查找某顶点的所有邻接点,时间复杂度是(B)。
A. O ( 1 ) O(1) O(1) B. O ( n ) O(n) O(n) C. O ( n + e ) O(n+e) O(n+e) D. O ( n 2 ) O(n^2) O(n2)
基于邻接矩阵存储的广度优先遍历图算法如下,请在横线处填写适当的语句或表达式。

void MGraph<DataType>::BFTraverse(int v)
{
	int w,j,Q[Maxsize];
	int front=-1,rear=-1;
	cout<<vertex[v];
	visited[v]=1;
	Q[++rear]=v; 
	while(front!=rear)
	{
		w=Q[++front]; 
		for(j=0;j<vertexNum;j++)
			if(____①____&& visited[jl==0)
			{
				cout<<vertex[jl;
				visited[j]=1;
				____②____
			}
	}
}

edge[w][j]==1

Q[++rear]=j;

图的邻接表存储结构及实现
图采用邻接表存储,空间复杂度只与顶点个数有关,和边数无关。(×)
在图的邻接表存储中,存在两类结点:顶点表结点和边表结点。(√)
无向图有n个顶点e条边采用邻接表存储,查找某顶点的所有邻接点,平均情况下的时间复杂度是(C)。
A. O ( n ) O(n) O(n) B. O ( n + e ) O(n+e) O(n+e) C. O ( e / n ) O(e/n) O(e/n) D. O ( e ) O(e) O(e)
某个有向图采用邻接表存储,其存储结构是唯一的。(×)
基于邻接表存储的深度优先遍历图算法如下,请在横线处填写适当的语句或表达式。

void ALGraph<DataType>:: DFTraverse(int v)
{
	int j; 
	EdgeNode *p=nullptr; 
	cout<<adjlist[v].vertex; 
	visited[v]=1;
	____①____
	while(p!=nullptr)
	{
		____②____
		if(visited[j]==0) 
		DFTraverse(j);
		____③____
	}
}

p=adjlist[v].firstEdge;

j=p->adjvex;

p=p->next;

Prim算法
无向图的生成树是该图的一个极小连通子图。(√)
Prim算法采用(C)作为存储结构。
A.顺序存储 B.链接存储 C.邻接矩阵 D.邻接表
对于如下图所示无向连通图,从顶点d出发用Prim算法构造最小生成树,回答下列问题:
在这里插入图片描述
(1)最小生成树的代价是(B)。
A.15 B.17 C.19 D.20
(2)加入最小生成树的第4条边是(D)。
A.(f,e)2
B.(b,e)3
C.(f,b)3
D.(b,a)5
Prim算法如何存储候选最短边集?
答:设数组adjvex[n]和lowcost[n]分别存储候选最短边的邻接点和权值。
对于上图所示无向网图,从顶点d出发用Prim算法构造最小生成树,请填写下表。
在这里插入图片描述
Prim算法如下,请在横线处填写适当的语句或表达式。

void Prim(int v)
{
	int i,j,k; 
	int adjvex[MaxSize],1owcost[MaxSize]; 
	for(i=0;i< vertexlum;i++)//初始化辅助数组
	{
		____①____adjvex[il=v;
	}
	lowcost[v]=0;
	for (k=1;k<vertexNum;i++)//迭代n-1次
	{
		j=MinEdge(lowcost,vertexNum)//寻找最短边的邻接点j
		cout<<j<<adjvex[j]<<lowcost[j]<<endl; 
		lowcost[j]=0;
		for(i=0;i<vertexNum;i++)//调整辅助数组
			if(____②____)
			{
				lowcost[i]=edge[i][jl;
				____③_____
			}
	}
}

lowcost[i]=edge[v][i];

edge[i]i]< lowcost[i]

adjvex[i]=j;

Kruskal算法
Kruskal算法采用(C)作为存储结构。
A.邻接矩阵 B.邻接表 C.边集数组 D.多重链表
并查集将集合中的元素组织成树的形式,并采用(A)存储。
A.双亲表示法 B.孩子表示法 C.二叉链表 D.列举法
有并查集{{a,b},{c,d,e},{f}},则双亲表示法的存储状态可能是(C)。
A.[-1,-1,2,2,2,3]
B.[-1,-1,2,2,2,5]
C.[-1,0,-1,2,3,-1]
D.[-1,0,-1,2,2,3]
对于如下图所示无向连通图,用Kruskal算法构造最小生成树,回答下列问题:
在这里插入图片描述
(1)加入最小生成树的第3条边是(D)。
A.(a,c)3
B.(b,e)3
C.(f,b)3
D.(d,f)4
(2)假设加入最小生成树的第2条边是(a,c)3,则当前的连通分量是(A)。
A.{a,c}{f,e}{d}{b}
B.{a,c}{f,e}{d,b}
C.{a,c,f,e}{d}{b}
D.{a,c,f,e}{d,b}
Kruskal 算法如下,请在横线处填写适当的语句或表达式。

void Kruskal()
{
	int num=0,i,vex1,vex2;
	int parent[vertexNum];//双亲表示法存储并查集
	for(i=0;i<vertexNum;i++)
		____①____//初始化n个连通分量
	for(num=0,i=0; num<vertexNum;i++)
	{
		vex1=FindRoot(parent, edge[i]. from);
		vex2=FindRoot(parent, edge[i]. to); 
		if()
		{
			cout<<edge[i].from<<","<<edge[i].to<< edge[i].weight;
			parent[vex2]=vex1;
			____③_____
		}
	}
}

parent[i]=-1;

vex1!=vex2

num++;

Dijkstra 算法
1.Dijkstra算法采用(C)作为存储结构。
A.边集数组
B.多重链表
C.邻接矩阵
D.邻接表
2.对于如下图所示有向图,用Dijkstra算法求最短路径,回答下列问题:
在这里插入图片描述

(1)从 v 0 v_0 v0 v 2 v_2 v2的最短路径长度是(D)。
A.30 B.25 C.26 D.22
(2)求得的第3条最短路径是(B)。
A. ( v 0 v 4 ) (v_0v_4) (v0v4) 11
B. ( v 0 v 6 v 3 ) (v_0v_6v_3) (v0v6v3) 13
C. ( v 0 v 4 v 3 ) (v_0v_4v_3) (v0v4v3) 18
D. ( v 0 v 6 v 3 v 5 ) (v_0v_6v_3v_5) (v0v6v3v5) 16
Dijkstra算法如何保存迭代过程中当前的最短路径长度?
用数组dist[n]保存当前的最短路径长度。
对于上图所示有向图,用Dijkstra算法执行过程中数据结构的中间结果,请填写下表。
在这里插入图片描述
Dijkstra算法如下,请在每个横线处填写适当的语句或表达式。

void Dijkstra(int v)//从源点v出发
{
	int i,k,num,dist[MaxSize];
	for(i=0;i<vertexNum;i++)//初始化数组dist
		____①____
	for(num=1;num< vertexNum;num++)
	{
		k=Min(dist,vertexNum); 
		cout<<v<<"-->"<<k<<":"<<dist[k]); 
		for(i=0;i<vertexNum;i++)
			if(_____②_____)
				dist[i]=dist[k]+ edge[k][il;
			dist[k]=0;//将顶点k加到集合S中
	}
}

dist[i]=edge[v][i];

dist[i]>dist[k]+edge[k][i]

Floyd算法
1.Floyd算法采用(A)作为存储结构。
A.邻接矩阵 B.邻接表 C.边集数组 D.多重链表
2.Floyd算法的时间复杂度是(C)。
A. O ( n ) O(n) O(n) B. O ( n 2 ) O(n^2) O(n2) C. O ( n 3 ) O(n^3) O(n3) D. O ( n 4 ) O(n^4) O(n4)
3.Floyd算法可以求任意两个顶点之间的最短路径。(√)
4.设有向图的邻接矩阵存储如下图左边所示,第2次迭代结果如下图右边所示,请在括号中填值。
在这里插入图片描述
①5
②9
③4

拓扑排序
1.在一个有向图的拓扑序列中,若顶点a在顶点b之前,则图中必有一条弧<a,b>。(×)
2.若一个有向图的邻接矩阵中对角线以下元素均为零,则该图的拓扑序列必定存在。(√)
3.拓扑排序算法可以用栈或者队列保存入度为0的顶点。(√)
4.在AOV网中不可能出现回路,因此一定存在拓扑序列。(×)
5.已知有向图带入度的邻接表存储如图6-12所示,画出拓扑排序算法在执行过程中,删除顶点 v 4 v_4 v4后邻接表的存储状态。
在这里插入图片描述
删除后:
在这里插入图片描述
拓扑排序算法如下,请在横线处填写适当的语句或表达式。

void TopSort()
{
	int i,j,k; 
	int S[MaxSize],top=-1; 
	EdgeNode *p=nullptr; 
	for(i=0;i<vertexNum;i++)
		if(____①____)
			S[t+top]=i;//将入度为0的顶点压栈
	while (top!=-1)
	{
		j=S[ top--]; 
		cout <<adjlist[j].vertex;
		_____②_____//工作指针p初始化
		while(p!=nullptr)
		{
			k=p->adjvex;
			____③____
			if(adjlist[k].in==0)
			{
				S[++top]=k; 
			}
			p=p->next;
		}
	}
}

adjlist[il.in=0

p=adjlist[i].firstEdge;

adjlist[k].in--;

关键路径
在AOE网中一定只有一条关键路径。(×)
加快关键活动的进度一定会缩短最短工期。(×)
关键活动的最早开始时间和最晚开始时间都不能推迟,否则会影响整个工期。(√)
求出所有事件的最早发生时间后,即可确定最短工期。(√)
如下图所示AOE网,回答下列问题:
在这里插入图片描述

(1)活动 a 2 a_2 a2的最早开始时间是(B),最晚开始时间是(B)。
A.3 B.4 C.5 D.6
(2)该AOE网的最短工期是(C)。
A.8 B.10 C.12 D.16
(3)该AOE网有(B)条关键路径。
A.1 B.2 C.3 D.4
(4)事件 v 2 v_2 v2的含义是什么?
活动 a 1 a_1 a1 a 2 a_2 a2已经结束,活动 a 3 a_3 a3可以开始。

参考资料:《数据结构(从概念到C++实现)》清华大学出版社,王红梅

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

oldmao_2000

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值