数据结构——图(1)很详细

图的内容太多了,分为两部分写

1>基本概念

定义

定义:图G由顶点集V和关系VR组成,记为G=(V, VR)。
V是顶点(元素)的有穷非空集。

有向图,弧(有向边)

若图G任意两顶点a,b之间的关系为有序对<a,b>,即<a,b>∈VR,则称<a,b>为从a到b的一条弧 / 有向边。其中a是<a,b>的弧尾,b是<a,b>的弧头;由顶点集和弧的集合组成的图成为有向图。
举个栗子
在这里插入图片描述

无向图,边(无向边)

由顶点集合与边的集合组成的图称为无向图。

举个栗子

在这里插入图片描述(a,b)表示a,b互为邻接点,(a,b)依附于a和b, (a,b)与a和b相互关联。

完全图(无向)

n代表顶点的数目,e代表边或弧的数目。
e的取值范围为0到n(n-1)/2。
在这里插入图片描述

有n个顶点和n(n-1)/2条边的无向图称为完全图

有向完全图

在这里插入图片描述
有n个顶点和n(n-1)条弧的有向图称为有向完全图。

边(弧)上加权的图。
带权值的无向图称为无向网,带权值的有向图称为有向网。
在这里插入图片描述

子图

对图G=(V,VR)和G’=(V’,VR’),若V’ V且VR’ VR,则称G’是G的一个子图。
在这里插入图片描述

如图,G1,G2,G3为G的子图,G4不是。

与顶点V相关联的边(x,y)的数目,称为V的度。
记作TD(V)或D(V)。

出度

以顶点V为弧尾的弧的数目称为V的出度,记作OV(V).
O指out

入度

以顶点V为弧头的弧的数目,称为V的入度,记作ID(V).
I指in
举个栗子
在这里插入图片描述

连通性术语

顶点Vi到Vj有路径是指存在一个顶点序列 Vi,Vi1,Vi2…Vim,Vj.
其中(Vi,Vi1)、(Vi1,Vi2)…(Vim, Vj)是图的边或弧。

连通图及连通分量(无向图G)

若以顶点Vi到Vj有路径则称Vi和Vj是连通的。
若图G任意两点是连通的,则称G是连通图。
若图G’是G的一个极大连通子图,则称G’是G的一个连通分量。
连通图的分量就是自身,非连通图有多个连通分量。
在这里插入图片描述
G1,G2,G3都是G的连通分量。

强连通图及强连通分量(有向图G)

若在图G中每对顶点Vi,Vj之间,从Vi到Vj且从Vj到Vi都存在路径,则称G是强连通图。
若图G’是G的一个极大连通子图,则称G’是G的强连通分量。强连通图的强连通分量是自身。
在这里插入图片描述
G1,G2是G的强连通分量。

生成树

设G=(V,E), G’=(V’,E’), 且V=V’,若G是连通图,G’是G的一个极小连通子图,则G’是G的一个生成数。
在这里插入图片描述

图的操作

CreateCraph(&G,V,VR)生成图
DestroyCraph(&G)销毁图
Locate(G,u)查找顶点u的位置
GetVex(G,v)读取顶点V的信息
PutVex(&G,v,value)给顶点V赋值value
FirstAdjVex(G,v)读v的第一个邻接点
NextAdjVex(G,v,w)读v(相对于w)的下一个邻接点
InsertVex(&G,v)插入顶点
DeletsVex(&G,v)删除顶点
DFSTraverse(G,vists())深度优先遍历
BFSTraverse(G,visit())广度优先遍历

2>图的存储

数组表示法

数组表示法用两个数组分别存储数据元素(顶点)的信息和数据元素之间的关系
顶点数组用一维数组存储顶点的(元素)
邻接矩阵用二维数组存储顶点(元素)之间的关系(边或弧),0表示无关,1表示有关

举个栗子

对无向图G
在这里插入图片描述

对有向图G
在这里插入图片描述

对与有向网和无向网,需要在邻接矩阵中加上关系的权值,如果两个顶点间有邻接关系,就用权值代替原来的1,否则用∞代替0。

举个栗子

在这里插入图片描述

数组表示法的数据类型定义

顶点数组vexs
邻接矩阵arcs
顶点数
图的类型
弧(边)数

code(伪)

#define MAX_VERTEX_NUM 20
typedef enum{DT, DN, UDG, UDN 
}GraphKind;
typedef struct{
 VertexTpye vexs[MAX_VERTEX_NUM];
 VRType arcs[MAX_VERTEX_NUM][MAX_VERTEX_NUM];
 int vexnum,arcnum;
 GraphKind kind;
}Mgraph; 

邻接表表示法

这是一种顺序 + 链式的物理存储结构,通过头结点数组保存所有的信息,用单链表保存顶点之间的关系。

无向图的邻接表

为图G的每个顶点建立一个单链表,第i个单链表中的结点表示依附于顶点的Vi的边。

为图G的每个顶点建立一个单链表,第i个单链表中的结点表示依附于顶点的Vi的边。
若无向图G有n个顶点和e条边,需要n个表头结点和2e个表结点
无向图G的邻接表,顶点Vi的度为第i个单链表的长度

在这里插入图片描述

有向图的邻接表

第i个单链表中的表结点的值为j,表示以顶点Vi为尾的一条弧(Vi, Vj)
若有向图G有n个顶点和条弧,则需n个表头结点和e个表结点
有向图G的邻接表顶点Vi 的出度为第i个单链表的长度
求顶点Vi的入度徐遍历全部单链表,统计结点值为i的结点数

在这里插入图片描述

有向图的逆邻接表表示法

第j个单链表中的表结点的值为i,表示以顶点Vi为弧尾的一条弧(Vi, Vj)
若有向图有n个顶点e条弧,则需n个表头结点和e个表结点
有向图G的逆邻接表,顶点Vi的入度为第i个单链表的长度
求顶点Vi的出度需遍历全部单链表,统计结点值为i的结点数

在这里插入图片描述

有向网的邻接表

表结点表示边或弧,所以就对表结点扩充一个属性域,表结点至少包含顶点序号权值下一个表结点指针3个属性。

在这里插入图片描述

由此终于可以得出邻接表是如何定义的了

code(伪)

#define MAX_VERTEX_NUM 20
typedef struct ArcNode{//表结点类型定义,对网需要加权值属性
	int adjvex;//顶点位置编号
	struct ArcNode *nextarc;//下一结点指针
	InfoType *info; 
}ArcNode;
typedef struct VNode{//头结点及其数组类型定义
	VertexType data;//顶点信息
	ArcNode *firstarc;//指向第一条弧 
}VNode,AdjList[MAX_VERTEX_NUM];
typedef struct {//邻接表的类型定义
	AdjList vertice;//头结点数组
	int vexnum,arcnum;//顶点数,弧数
	GraphKind  kind;//图的类型 
}ALGraph;

有向图的十字链表

1)每条弧有一个弧结点
在这里插入图片描述

tailvex:弧尾的位置
headvex:弧头的位置
hlink:指向下一条弧头相同的弧
tlink:指向下一条弧尾相同的弧
2)每个顶点有一个顶点结点
在这里插入图片描述
data:顶点信息
firstin:指向以该顶点为弧头的第一条弧
firstout:指向以该顶点为弧尾的第一条弧
在这里插入图片描述

以邻接图为基础,扩展结点的属性成起止结点序号
再添加逆邻接表的信息

在这里插入图片描述
这个图的画法还是有技巧的,眼尖的同学可能注意到了,我在每个弧结点的下方有有标记,邻接表的指向和标记的顺序是相同的,AB就是AB,而逆邻接表刚好和标记相反,AB则为BA。

code(伪)

#define MAX_VERTEXT_NUM 20
typedef struct ArcBox{
	int tailvex,headvex; //该弧的尾和头顶点位置
	struct ArcBox *hlink,*tlink;//分别为弧头相同和弧尾相同的弧的链域 
}ArcBox;
typedef struct VexNode{
	VertexType data;
	ArcBox *firstin,*firstout;//分别指向该顶点的第一条入弧和出弧 
}VexNode;
typedef struct{
	VexNode xlink[MAX_VERTEXT_NUM];//表头变量
	int vexnum,arcnum;//有向图的当前顶点数和弧数 
}OLGraph;

无向图邻接多重表

1)每个顶点有一个头结点

datafirstedge

data:顶点信息
firstedge:指向第一条依附于该顶点的边

2)每一条边有一个表结点
在这里插入图片描述

mark:标志域,可以标记该边是否被搜索过
ivex,jvex:值该边依附的两个顶点在顶点数组的位置
ilink:指向下一条依附于顶点vi的边
jlink:指向下一条依附于顶点vj的边
在这里插入图片描述

code(伪)

//无向图邻接多重表 
#define MAX_VERTEXT_NUM 20
typedef enum{ unvisited,visited}visitedIf;
typedef struct EBox{
	visitedIf mark;//访问标记
	int ivex,jvex;//该边依附的两个顶点的位置
	struct EBox *ilink,*jlink;//分别指向依附与这两个顶点的下一条边 
}EBox;
typedef struct VexBox{
	VertexType data;
	EBox *firstedge;//指向第一条依附于该顶点的边 
}VexBox;
typedef struct{
	VexBox adjmulist[MAX_VERTEXT_NUM];
	int vexnum,edgenum;//无向图的当前顶点数和边数 
}AMLGraph;

3>图的遍历

图的深度遍历

从V1出发,访问一个未访问过的V1的邻接点V2,以V2作为新的出发点,继续深度优先搜索,直到把与V1相连通的点都被访问,如果还有未访问的点,需要在其中选择一个作为新的起点,继续进行深度优先搜索,直到所有顶点都被访问到。

举个栗子
在这里插入图片描述

code(伪)

//深度优先遍历 
boolean visited [MAX]
void DFDTraverse(Graph G, Status (*visit)())
{
	for(v=0;v<G.vesnum;v++)//初始化各顶点未访问状态
		visited[v]=false;
	for(v=0;v<G.vexnum;v++)
		if(!visit[v])//从一个未访问的顶点开始
			DFS(G,v,visit); 
}
void DFS(Graph G,int v,Status (*visit)())
{
	visited[v]=true;visited(v);//标记顶点,并且访问顶点 
	for(w=FirstAdjVex(G,v),w>0;w=NextAdjVex(G,v,w))//查找与顶点v相邻并且没有访问过的顶点 
		if(!visited[w])//处理所有未访问的邻接顶点 
			DFS(G,w,visited); //递归,继续深度优先遍历 
}

图的广度优先遍历

假定从V1出发,先访问V1,再访问所有未被访问过的V1的邻结点,访问次序满足:先被访问过的顶点的邻接点的访问先于后被访问过的顶点的邻结点的访问。
直到与V1相连通的点都被访问。如果还有未被访问过的顶点,选择其中一个作为出发点,继续广度优先搜索,直到所有的顶点被访问。

举个栗子

在这里插入图片描述

code(伪)

//广度优先遍历
void BFSTraverse(Graph G,Status (*visit)())
{
	for(v=0;v<G.vexnum;v++)
		visited[v]=false;	//初始化visited数组 
	InitQueue(Q);//初始化队列
	for(v=0;v<G.vexnum;v++) //按顶点位置序号依次选择顶点
		if(!visited[v]){//遇到未访问过的顶点开始遍历
			visited[v]=true;visit(v);EnQueue(Q,v);//标记顶点,并访问顶点,把顶点加入到队列 Q中 
			while(!QueueEmpty(Q)){//如果队列非空,进行出队操作 
				DeQueue(Q,u);//出队顶点为u 
				for(w=FirstAdjVex(G,u),w>=0;w=NextAdjVex(G,u,w))//对u的相邻顶点进行分析 
					if(!visited[w]){//如果某个相邻顶点w没有被访问过 
						visited[w]=true;//标记顶点 
						visited(w);//开始访问 
						EnQueue(Q,w);//把w入队 
					}
			}
				
		} 
 } 

4>图的连通性问题

无向图的连通分量和生成树

遍历几次就有几个连通分量在这里插入图片描述
DFS生成树
对一个无向连通图,进行DFS遍历时,遍历过程中经过的点和边组成的图,称为DFS生成树。
在这里插入图片描述

BFS生成树

在这里插入图片描述

DFS生成森林
对一个非连通图,由于需要多次遍历才能访问到所有顶点,每次DFS遍历都得到一课树所以得到一个深度优先生成森林。
在这里插入图片描述
BFS生成森林
对一个非连通图,由于需要多次遍历才能访问到所有顶点,每次BFS遍历都得到一课树所以得到一个广度优先生成森林。
在这里插入图片描述

有向图的强连通分量

在有向图G中,从某个顶点V出发,顺着弧的方向进行DFS遍历,得到顶点集合V1,再从顶点V出发,逆着弧的方向进行DFS遍历,得到顶点集合V2。就可以得到一个强连通分量Gs=(Vs,VRs),其中:
Vs=V1∩V2;VRs为Vs中所有顶点在G中的弧
将得到的强连通分量从图中去掉。剩下的在重复上述操作,则可以求出全部强连通分量。在这里插入图片描述

网的最小生成树

先来举个栗子:
假如现在又n个城市,要建立一个通信网,要连通n个城市,只需要n-1条线路,每条线路直接连通两个城市,城市与城市之间可以进行中继。如何建立这个通信网,使得经费最省。也就是说,如何建立n-1条线路,使得总的费用最低,并且任意两个城市之间可以互相通信。
我们可以把城市看成顶点,线路看成带权值的边,每一种方法可以看成一颗生成树,问题就成了如何求解一颗生成树使得费用最低,也就是各边权值之和具有最小值。
在网G的生成树中,其中各边的权之和最小的生成树,称为G的最小代价生成树,也就是最小生成树。
在这里插入图片描述

MST性质

设G={V,E}是一个连通网,U是V的一个非空子集。如果边(u,v)是G中所有一端在U中(即u∈U),而另一端在V-U中(即v∈V-U),具有最小权值的一条边,则由一颗包含边(u,v)的最小生成树。
在这里插入图片描述

普里姆算法(prime算法),求最小生成树(code伪)

对n个顶点的连通网,初始时,T=(U,T,E),U中只有一个开始顶点U0,TE=ψ,以后根据MST性质,每增加一个顶点和一条边,重复n次。U不断增大,V-U不断减小直到为空。
开始可能存在多条具有最小权值的边,选择次序不同,故最小生产树不一定唯一。

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

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

在这里插入图片描述
在这里插入图片描述怎么用计算思维表达这一过程,接着就是借助邻接矩阵和closedge这样一个结构体数组。
在这里插入图片描述
在这里插入图片描述

图终于画完了,接着给出伪代码

//图的最小生成树,prime算法 
struct{
	VertexType adjvex;	//最小权值的边,在U中依附的顶点 
	VRType lowcost;		//记录最小权值lowcost 
}closedge[MAX_VERTEXT_NUM];
void Prime(MGraph G, VertexType u)
{
	k=LocateVex(G,u);	//确定起始顶点u的位置序号
	for(j=0;j<G.vexnum;j++)//初始化最短边权值和依附顶点值 
		if(j!=k)
			closedge[j]={u,G.arcs[k][j].adj}	
	closedge[k].lowcost=0;//选定起点u到U中 
	for(i=1;i<G.vexnum;i++){//依次加入n-1个顶点、n-1条边 
		k=minimum(closedge);//选择下一条最短边对应的顶点序号
		printf(closedge[k].adjvex,G.vexs[k]);//输出生成树的边
		closedge[k].lowcost=0;		//顶点k加入到U中
		for(j=0;j<G.vexnum;j++)		//替换最小权值的依附顶点 
			if(G.arcs[k][j].adj<closedge[j].lowcost)
				closedge[j]={G.vexs[k],G.arcs[k][j].adj} 
	}
} 
//算法适合边稠密的无向连通图 ,T(n,e)=O(n*n)

5>

学习进度到了图这一课,本来我是打算把数据结构从头到尾整理一遍,但刚好学到了图,就打算先把图的笔记先写出来。

从单链表到树,搞懂之后觉得数据结构学起来很爽,直到我遇见了图,图这玩意太难了,这篇笔记写着写着让我想要放弃,但写了这么多,不写了实在可惜。

一开始只是感jio有知识盲区
在这里插入图片描述
接着越来越难懂了
在这里插入图片描述
莫得办法,只能自己反复看,看懂了然后从草稿箱拿出这篇笔记接着写

看一下字数:在这里插入图片描述

在这里插入图片描述
写还不是最难的,举例子是要画图的,做图又不会做,盗图又不会盗,就只能靠一笔一画去画这种东西,才能勉强举的了栗子这样子。
在这里插入图片描述

总共画了42张图
在这里插入图片描述

这么多东西自己都写不下去了,让大家怎么看,于是我先写了前4节,后面再写有向无环图及其应用和最短路径

已经尽力去学了,如果有些错误,还请包涵并指出。

到这里终于写完第一部分了。

在这里插入图片描述

  • 15
    点赞
  • 44
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

unseven

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

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

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

打赏作者

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

抵扣说明:

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

余额充值