第六章:图

6.1 图的定义

图G由顶点集V和边集V组成,记为G=(V,E),其中V(G)表示图G中顶点的有限非空集;E(G)表示图G中顶点之间的边集合。
若V={v1,v2,…,vn},则用|V|表示图G中顶点的个数,也称图G的阶;用|E|表示图G中的边数。

有向图:
G1=(V1,E1)
V1={A,B,C,D,E}
E1={<A,B>,<A,C>,<A,D>,<A,E>,<B,A>,<B,C>,<B,E>,<C,D>}
注:<v,w>与<w,v>方向相反

无向图:
G2=(V2,E2)
V2={A,B,C,D,E}
E2={(A,B),(B,D),(B,E),(C,D),(C,E),(D,E)}

简单图:不存在重复边,不存在顶点到自身的边
多重图:图G中某两个结点之间的边数多于一条,又允许顶点通过同一条边和自己关联

顶点的度,入度,出度:
对于无向图,顶点v的度是指依附于该顶点的边的条数,记为TD(v),全部顶点的度的和等于边数的两倍
对于有向图,入度是以顶点v为终点的有向边的条数,记为ID(v);出度是以顶点v为起点的有向边的条数,记为OD(v);顶点v的度等于其入度和出度之和

顶点间的关系描述
在这里插入图片描述

若图G中任意两个顶点都是连通的,则称图G为连通图,否则为非连通图。
对于n个顶点的无向图G
若G为连通图,则最少有n-1条边
若G是非连通图,则最多可能有C2n-1条边

若图G中任意两个顶点都是强连通的,则称图G为强连通图。
对于n个顶点的有向图G
若G是强连通图,则最少有n条边(形成回路)

子图:
在这里插入图片描述

无向图中极大连通子图称为连通分量
有向图中极大强连通子图称为有向图的连通分量

连通图的生成树是包含图中全部顶点的一个极小连通子图。
若图中顶点数为n,则它的生成树有n-1条边;对生成树而言,若砍去它的一条边,则会变成非连通图,若加上一条边则会形成回路

在非连通图中,连通分量的生成树构成了非连通图的生成森林

边的权:
在这里插入图片描述

无向完全图:无向图中任意两个顶点之间都存在边
有向完全图:有向图中任意两个顶点之间都存在方向相反的两条弧

边数很少的图称为稀疏图,反之称为稠密图

树:不存在回路,且连通的无向图
有向树:一个顶点的入度为0,其余顶点的入度均为1的有向图

6.2 图的存储

6.2.1 邻接矩阵

#define MaxVertexNum 100//顶点数目最大值
typedef struct{
	char Vex[MaxVertexNum];//顶点表
	int Edge[MaxVertexNum][MaxVertexNum];//邻接矩阵,边表
	int vexnum,arcnum;//图当前顶点数和边数/弧数
}MGraph;

在这里插入图片描述
在这里插入图片描述

邻接矩阵存储带权图:
在这里插入图片描述
邻接矩阵性能分析:
空间复杂度O(|v|2)——只和顶点数相关,和实际边数无关
适合存储稠密图
无向图的邻接矩阵是对称矩阵,可以压缩存储
在这里插入图片描述

邻接矩阵性质:
在这里插入图片描述

6.2.2邻接表

#define MaxVertexNum 100//图中顶点数目最大值
typedef struct ArcNode{//边表结点
	int adjvex;//该弧所指向的顶点的位置
	struct ArcNode *next;//指向下一条弧的指针
	//InfoType info;//网的边权值
}ArcNode;
typedef struct VNode{//顶点表结点
	VertexType data;//顶点信息
	ArcNode *first;//指向第一条依附该顶点的弧的指针
}VNode,AdjList[MaxVertexNum];
typedef struct{
	AdjList vertices;//邻接表
	int vexnum,arcnum;//图的顶点数和弧数
}ALGraph;//ALGraph是以邻接表存储的图类型

邻接表与邻接矩阵的比较
在这里插入图片描述

6.2.3十字链表

十字链表只适合存储有向图
在这里插入图片描述

6.2.4邻接多重表

邻接多重表只适合存储无向图
在这里插入图片描述
在这里插入图片描述

6.3 图的遍历

6.3.1广度优先(BFS)

bool visited[MaxVertexNum];//访问标记数组
void BFSTraverse(Graph G){//对图G进行广度优先遍历
	for(i=0;i<G.vexnum;++i)
		visited[i]=false;//访问标记 数组 初始化
	InitQueue(Q);//初始化辅助队列Q
	for(i=0;i<G.vexnum;++i)//从0号顶点开始遍历
		if(!visited[i])//if(visited[i]==false)
			BFS(G,i);//i未被访问,从i开始访问
}
void BFS(Graph G,int v){//从顶点v出发,广度优先遍历图G
	visit(v);//访问初始结点v
	visited[v]=true;//对v做以访问标记
	EnQueue(Q,v);//v入队
	while(!isEmpty(Q)){
		DeQueue(Q,v);//顶点v出队
		for(w=FirstNeighbor(G,v);w>=0;w=NextNeighbor(G,v,w))
		/*检测v所有邻接点
		FirstNeighbor(G,x):求图G中顶点x的第一个邻接点,若有则返回顶点号
		NextNeighbor(G,x,y):假设y是x的第一个邻接点,则返回除y外的第一个邻接点*/
		if(!visited[w]){//w为v的尚未访问的邻接点
			visit(w);//访问顶点w
			visited(w)=true;//对w做以访问标记
			EnQueue(Q,w);//顶点w入队
		}//if
	}//while
}
/*第二个函数可访问连通图,两个函数访问非连通图*/

复杂度分析:
空间复杂度:由辅助队列决定最坏O(|V|)
时间复杂度:
在这里插入图片描述
广度优先生成树:邻接表存储则表示方式不唯一
在这里插入图片描述
非连通图可形成广度优先生成森林。

6.3.2 深度优先(DFS)

bool visited[MaxVertexNum];//访问标记数组
void DFSTraverse(Graph G){//对图G进行深度优先遍历
	for(v=0;v<G.vexnum;++v)
		visited[v]=false;
	for(v=0;v<G.vexnum;++v)
		if(!visited[v])
			DFS(G,v);
}
void DFS(Graph G,int v){//从顶点v出发深度优先遍历图G
	visit(v);//访问顶点v
	visited[v]=true;
	for(w=FirstNeighbor(G,v);w>=0;w=NextNeighbor(G,v,w))
		if(!visited[w])//w为v的尚未访问的邻接点
			DFS(G,w);
}

复杂度分析:
空间复杂度:来自函数调用栈,最坏情况,递归深度O(|V|);
时间复杂度:
在这里插入图片描述
广度优先生成序列与生成树:
在这里插入图片描述
在这里插入图片描述

6.4 图的应用

6.4.1 最小生成树

对于一个带权连通无向图G=(V,E),生成树不同,每棵树的权也可能不同。权最小的数即为最小生成树。
Prim算法 v.s. Kruskal算法:
在这里插入图片描述

6.4.2 最短路径

广度优先(BFS)算法求无权图最短路径:
在这里插入图片描述
在这里插入图片描述
Dijkstra算法求带权图最短路径:
在这里插入图片描述
此算法不适合求负权值最短路径

Floyd算法:
求出每一对顶点之间的最短路径
时间复杂度O(|V|3)

6.4.3 有向无环图描述表达式

有向无环图:不存在环的有向图
描述表达式:
在这里插入图片描述
在这里插入图片描述

6.4.4 拓扑排序

AOV网:用顶点表示活动的网(有向无环图)

拓扑排序的实现:
①从AOV网中选择一个没有前驱(入度为0)的顶点输出
②从网中删除该顶点和所有以它为起点的有向边
③重复①②直到当前的AOV网为空或当前网中不存在无前驱的顶点为止

逆拓扑排序的实现:
①从AOV网中选择一个没有后继(出度为0)的顶点输出
②从网中删除该顶点和所有以它为终点的有向边
③重复①②直到当前的AOV网为空为止

6.4.5 关键路径

在这里插入图片描述
在这里插入图片描述
求关键路径步骤:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

终究成为社畜

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

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

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

打赏作者

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

抵扣说明:

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

余额充值