六、 图

本文详细介绍了图的概念、特殊图、存储方式(邻接矩阵、邻接表等)、基本操作、广度优先遍历(BFS)、深度优先遍历(DFS)、最小生成树与最短路径算法(Prim、Kruskal、Dijkstra)。涵盖了无向图、有向图、权重、连通性及各种遍历策略的实现与分析。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、图的概念

(一)定义:

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

注意:图不可以是空图,即V一定是非空集:
在这里插入图片描述## (一)无向图:
没有方向的图,E是无向边(边),边是顶点的无序对,即为(v,w)或(w,v),且(v,w) = (w,v),其中v,w是顶点,边(v,w)依附于顶点w和v
在这里插入图片描述

(二)有向图:

有方向的图,E是有向边(弧),弧是顶点的有序对,记为<v,w>,其中v,w是顶点,v称为弧尾,w称为弧头,<v,w>称为从顶点v到顶点w的弧,且<v,w> != <v,w>
在这里插入图片描述

(三)简单图:

不存在重复的边且不存在顶点到自身的边

1.简单无向图:

2.简单有向图:

(四)多重图(考研不考):

图中某两个结点之间的边数多于一条,且允许顶点通过同一条边和自己关联

1.多重无向图:

2.多重有向图:


(五)几个基本概念:


顶点的度:
  对于无向图:顶点v的度是指依附于该结点的边的条数,记为TD(v)
  对于有向图:顶点的度 = 入度 + 出度,需探讨的是入度和出度,如下:


入度:以顶点v为终点的有向边的数目,记为ID(v)


出度:以顶点v为起点的有向边的数目,记为OD)(v)


路径:顶点vp到顶点vq之间的一条路径,指顶点序列


回路(环):第一个顶点和最后一个顶点相同的路径


简单路径:在路径序列中,顶点不重复出现的路径


简单回路:处第一个顶点和最后一个顶点外,其他顶点不重复出现的回路


路径长度:路径上边的数目


顶点到顶点的距离:从顶点u到顶点v的最短距离,若存在则为从u到v的距离,若不存在则距离无穷


连通:在无向图中,若顶点v到顶点w有路径存在,则v和w是连通的
连通图:在无向图中,任意两个顶点都是连通的
    连通图至少有n-1个边
    非连通图至少有C2n-1条边


强连通:在有向图中,若从顶点v到顶点w和从顶点w和顶点v都有路径,则v和w是强连通的
强连通图:在有向图中,任何一对顶点都是强连通
    对于n个结点的有向图G,若G是强连通图,则至少有n条边(形成回路)


子图:两个图的顶点集的子集边集的子集构成的图,满足图的条件和定义


连通分量:无向图中的极大连通子图
在这里插入图片描述


强连通分量:有向图中的极大强连通子图
在这里插入图片描述


生成树:连通图的生成树包含图中全部顶点的一个极小连通子图
在这里插入图片描述若图中顶点数为n,则它的生成树含有n-1条边,若去掉他的一条边,则变成非连通图,若加上一条边,则形成回路


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


边的权:在一个图中,每条边都标记上具有某种含义的数值,该数值称为该边的权值


带权的图/网:边上带有权值的图


带权路径长度:当图是带权图时,一条路径上所有边的权值之和


二、几种特殊的图


无向完全图:无向图中任意两个顶点之间都存在边
在这里插入图片描述


有向完全图:有向图中任意两个顶点之间都存在方向相反的两条弧
在这里插入图片描述


稀疏图:边很少的图,|E| < |V|log|V|


稠密图:边很多的图,|E| > |V|log|V|


树:不存在回路且连图的无向图


若n个顶点的图,|E| > n-1,则一定有回路


有向树:一个顶点的入度为0,其余顶点的入度均为1的有向图


三、图的存储

(一)邻接矩阵法:

1.邻接存储法存储不带权图:

在这里插入图片描述1表示有邻接(有边),0表示没有邻接(没有边)

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

无向图:
    第i个顶点的度 = 第i行(或第i列)的非零元素的个数
有向图:
    第i个结点的出度 = 第i行的非零元素个数
    第i个结点的入度 = 第i列非零元素的个数
    第i个结点的度 = 第i行和第i列非零元素的个数之和

2.邻接存储法存储带权图(网):

在这里插入图片描述

#define MaxVertexNum1 100//顶点数目的最大值
#define INFINITY 最大的int//宏定义常量无穷
typedef char VertexType;//顶点的数据类型
typedef int EdgeType;//带权图中边上权值的数据类型
typedef struct{
	VertexType Vex[MaxVertexNum1];//顶点
	EdgeType Edge[MaxVertexNum1][MaxVertexNum1];//带权的边
	int vexnum;//图的当前定点数
	int arcnum;//图的弧数 
}MGraph1; 

3.邻接矩阵表示法性能分析:

空间复杂度:O(|V|2) ,之和顶点数有关,和实际的边数无关
时间复杂度高:O(n2),不适合稀疏图,适合用于存储稠密图
无向图的邻接矩阵是对称矩阵,可以压缩存储,即只存储上三角区/下三角区

(二)邻接表(顺序+链式存储):


在这里插入图片描述

typedef struct ArcNode{//边/弧 
	int adjvex;//边/弧指向哪一个结点 
	struct ArcNode* next;//指向下一条弧的指针 
}ArcNode;
 typedef struct VNode{//顶点 
 	VertexType data;//顶点信息
	 ArcNode* frist;//第一条边/弧 
 }VNode,AdjList[MaxVertexNum1];  
 typedef struct{//用邻接表存储的图 
 	AdjList vertices;
 	int vexnum;
 	int arcnum;
 }ALGraph; 

在这里插入图片描述


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


(三)十字链表(存储有向图):

在这里插入图片描述

(四)邻接多重表(存储无向图):

在这里插入图片描述

四、图的基本操作

(一)Adjacent(G,x,y):

判断图G是否存在边<x,y>或(x,y)

(二)Neighbors(G,x):

列出图G中与结点x邻接的边

(三)InsertVertex(G,x):

从图G中插入顶点x

(四)DeleteVertex(G,x):

从图G中删除顶点x

(五)AddEdge(G,x,y):

若无向边(x,y)或有向边<x,y>不存在,则向图G中添加该边

(六)RemoveEdge(G,x,y):

若无向边(x,y)或有向边<x,y>存在,则向图G中添加该边

(七)FirstNeighbor(G,x):

求图G中顶点x的第一个邻接点,若有则返回顶点号,若没有或图中不存在x,则返回-1

(八)NextNeighbor(G,x,y):

假设图G中顶点y是顶点x的一个邻接点,返回除y之外顶点x的下一个邻接点的顶点号,若y是x的最后一个邻接点,返回-1

(九)Get_edge_value(G,x,y):

获取图G中边(x,y)或<x,y>对应的权值

(十)Set_edge_value(G,x,y,v):

设置图G中边(x,y)或<x,y>对应的权值为v

五、广度优先遍历(BFS)

(一)与树的广度优先遍历(层序遍历)之间的练习:

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

(二)算法实现:

bool visited[MaxVertexNum1];//访问标记数组
void BFG(Graph G,int v){//从顶点v出发,广度优先遍历图G 
	visit(v); //访问初始结点 
	visited[v] = TRUE;//对v做已访问标记
	Enqueue(Q,v); //顶点v入队列Q
	while(!isEmpty(Q)){
		DeQueue(Q,v);//顶点v出队列
		for(w = FirstNeighbor(G,v);w >= 0;w = NextNeighbor(G,v,w)){//检测v所有邻接点 
			if(!visited[w]){//w为v的尚未访问的邻接点
				visit(w);//访问结点w
				visit[w] = TRUE;//对w做已访问标记
				EnQueue(Q,w);//顶点w入队 
				
			} 
		} 
	} 
} 

在这里插入图片描述

(三)复杂度分析:

在这里插入图片描述

六、深度优先遍历(DFS)

(一)算法实现:

bool visited[MaxVertexNum1];//访问标记数组
void DFS(Grph G,int v){
	visit(v);
	visited[v] = TRUE;
	for(w = FirstNeighbor(G,v);w >= 0;w = NextNeighbor(G,v,w)){
		if(!visited[w]){
			DFS(G,w);
		}
	} 
}

在这里插入图片描述

(二)复杂度分析:

在这里插入图片描述

七、最小生成树

(一)概念:

所有地方都连通,且成本尽可能低
在这里插入图片描述

(二)Prim算法:

从某一个顶点开始创建生成树,每次将代价最小的新顶点纳入生成树,知道纳入所有为止

(三)Kruskal算法:

每次选择一条权值最小的边,使这条边的两头连通,知道所有结点连通

八、最短路径问题

(一)单源最短路径:

1.BFS算法(无权图):

在这里插入图片描述

2.Dijkstra算法(带权图、无权图):

在这里插入图片描述

(二)各顶点的最短路径:

1.Floyd算法(带权图、无权图):

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值