数据结构知识点-图

完全图:任意两个点都有一条边相连,无向完全图有n(n-1)/2条边,有向完全图n(n-1)条边。
在这里插入图片描述
顶点的度
在有向图中,顶点的度等于该顶点的入度与出度之和。
当有向图中仅有一个顶点的入度为0,其余顶点的入度均为1是一颗有向树。

连通图(强连通图)
无向图中,若任何两个顶点u、v都存在从u到v的路径,则称为连通图。
有向图中,若任何两个顶点u、v都存在从u到v的路径,则称为强连通图。

在这里插入图片描述无向图的极大连通子图成为G的连通分量,
极大连通子图是该子图是G的连通子图,将G的任何不再该子图中的顶点加入,子图不再连通。

图的存储
顺序存储结构:数组表示法、邻接矩阵
链式存储结构:多重链表、邻接表、邻接多重表、十字链表

邻接矩阵:如果图G有n个顶点,邻接矩阵是一个二维数组A[n][n]
无向图的邻接矩阵表示法
1、无向图的邻接矩阵是对称的
2、顶点i的度等于第i行(列)中1的个树
3、完全图的邻接矩阵中,对角元素为0,其余为1
在这里插入图片描述
有向图的邻接矩阵表示法
1、有向图的邻接矩阵可能是不对称的
2、顶点的出度等于第i行元素之和,顶点的入度等于第i列元素之和,顶点的度等于第i行元素和加第i列元素和。
在这里插入图片描述
邻接矩阵表示法的特点
优点:容易实现图的操作,如求某个顶点的度、判断顶点之间是否有边,找顶点的邻接点
缺点:n个顶点需要n*n个单元存储边,空间效率为O(n^2),对于稀疏图尤其浪费空间

邻接表表示法
每个顶点建立一个单链表,把与边Vi有关联的边的信息链接起来,每个结点设为三个域。
每个单链表有一个头结点设为两个域,每个单链表的头结点另外用顺序存储结构存储。
在这里插入图片描述无向图的邻接表表示法
邻接表不唯一,因为各个边结点的链入顺序是任意的,空间效率为O(n+2e),n个表头结点,2e个表结点.
在这里插入图片描述有向图的邻接表示法
出度等于单链出边表中链接的结点数
入度等于邻接点域为Vi的个数
在这里插入图片描述

邻接表表示法的特点
优点:空间效率高,容易找到顶点的邻接点。
缺点:判断两顶点是否有边或者弧,需要搜索两结点对应的链表。

邻接矩阵和邻接表表示法的关系
联系:邻接表中每个链表对应邻接矩阵中的一行,链表中的结点个数等于一行中非零元素的个数。
区别:对于任一确定的无向图,邻接矩阵是唯一的,但是邻接表不唯一。邻接矩阵的空间复杂度为O(n^2),邻接表的空间复杂度为O(n+e)
用途:邻接矩阵用于稠密图,邻接表用于稀疏图

十字链表
十字链表多用于有向图
在这里插入图片描述
在这里插入图片描述
图的遍历
深度优先搜索:一条路走到黑,和树的先序遍历类似,使用栈递归的访问。访问结点V,若V的第一个结点没有被访问过,深度遍历此邻接点,若当前邻接点已经被访问过,在找v的第二个邻接点重新访问。开辟一个visited数组,每访问一个结点就置为1。

算法思想:
从图中某个顶点v出发,访问v并置visited[v]的值为true,然后将v进栈,

# 使用邻接矩阵
void DFS(AMGraph G,int v)
{
	cout<<v;
	visited[v]=true;
	for(w=0;w<G.vexnum;w++)
			if((G.arcs[v][w]! = 0) && (!visited[]w))
				DFS(G,w);#w是v的邻接点,如果w未被访问,则递归调用DFS
}
# 使用邻接表
void DFS(ALGraph G,int V)
{
	cout<<v;
	visited[v]=true;
	p=G.vertices[v].firstarc;#指向边链表的第一个边结点
	while(p!=NULL)
	{
		w=p->adjvex; #w是v的邻接点
		if(!visited[w]) #如果w没有被访问过,递归调用DFS
			DFS(G,w);
		p=p->nextarc;# 访问下一个边结点
	}
}

用邻接矩阵表示图,遍历图中的每一个顶点都要从头开始扫描,时间复杂度为O(n^2)
用邻接表表示图,虽然有2e个表结点,但是只需要扫描e个即可完成,加上访问n个头结点的时间,时间复杂度为O(n+e)

广度优先搜索:在访问起点v后,以此访问v的邻接点;然后依次访问这些顶点中未被访问过的邻接点;直到所有顶点都被访问过为止。
广度优先搜索是一种分层的搜索过程,每向前走一步可能访问一批顶点,不像深度优先搜索那样有回退,因此广度优先搜索不是一个递归的过程,广度优先搜索使用的是队列,类似于树的层次遍历。

算法思想:
从图中某个顶点v出发,访问v并置visited[v]的值为true,然后将v进队;
只要队列不空,就重复执行(1)队头顶点u出队(2)依次检查u的所有邻接顶点w,如果visited[w]的值为false,则访问w,并置visited[w]的值为true,然后入队。

void BFS(Graph G,int v)
{
	cout<<v;
	visited[v]=true;
	InitQueue(Q);
	EnQueue(Q,v);
	while(!QueueEmpty(Q))#如果队列非空
	{
		DeQueue(Q,u);#队头元素出队
		for(w=FirstAdjvex(G,u);w>=0;w=NextAdjVex(G,w,w))
			if(!visited[w])#如果w为u没有访问过的邻接顶点
			{
				cout<<w;
				visited[w]=true;
				EnQueue(Q,w);#入队
			}
	}
}

DFS和BFS算法比较
空间复杂度相同都是O(n),DFS借用了堆栈,BFS借用了队列。
时间复杂度与存储结构有关,使用邻接矩阵时间复杂度为O(n^2),使用邻接表时间复杂度为O(n+e).

图的应用

最小生成树
极小连通子图:该子图是G的连通子图,在该子图中删除任何一条边,子图不在连通。
生成树:包含图G所有顶点的极小连通子图(n-1条边)。
在这里插入图片描述求最小生成树
使用不同的遍历图的方法可以得到不同的生成树
从不同的顶点出发,也可能得到不同的生成树
按照生成树的定义,n个顶点的连通图网络的生成树有n个顶点,n-1条边

构造最小生成树准则
必须只使用该网中的边来构造最小生成树
必须使用且仅使用n-1条边,来联结网络中的n个顶点
不能使用产生回路的边

求最小生成树算法
Prim算法:归并顶点,与边无关,适合稠密网。
1、从顶点u0出发,选择与它关联的具有最小权值的边,将顶点加入到生成树的顶点几何U中。
2、每一步从一个顶点在U中,而另一个顶点不再U中的各条边中选择权值最小的边,把他的顶点加入到U中。
3、直到所有顶点都加入到生成树顶点集合U中。
在这里插入图片描述
Kruskal算法:归并边,与点无关,适合稀疏网。
1、构造一个只有n个顶点,没有边的非连通图,每个顶点组成一个连通分量。
2、在E中选择最小权值的边,若该边的两个顶点落到不同的连通分量上,则加入T中,否则舍弃重新选择。
3、重复下去,直到所有顶点都在同一连通分量上。
在这里插入图片描述

最短路径
Dijkstra:单源最短路径,求一个顶点到其余各个顶点指点的。
1、初始化:先从源点V0到各个重点Vk的直达路径,即通过一条弧到达的路径。
2、选择:从这些路径中找到长度最短的路径。
3、更新:然后对其他的各条路径进行调整。
若在图中存在弧(u,vk),且(v0,u)+(u,vk) < (v0,vk)则以路径(v0,u,vk)代替(v0,vk)

在这里插入图片描述在这里插入图片描述1、首先初始化矩阵找到V0到所有相邻(一条路可以到达的结点)进行初始化,从初始化的数组路径中找到与v0结点路径最短的结点,添加到S[]中(S[]表示已经已经被确定最短路径的结点)。
2、计算当添加到一个结点后到达其他结点的最短路径,如果计算后的路径比之前的路径短则更新,如果长则不变。从v0到各个结点的最短路径中找到最短路径的结点,添加到S[]数组中。
3、以此执行以上步骤,即可按照路径递增的顺序求出v0到各个结点的最短路径了。

在这里插入图片描述

Floyd:所有顶点间的最短路径,任意两项之间的。

有向无环图的应用
AOV(Activity On Vertices)用顶点表示活动的网络
AOE(Activity On Edges)用边表示活动的网络

拓扑排序
1、输入AOV网,令n为顶点的个数。
2、在AOV网络中选择一个没有直接前驱的顶点进行输出。
3、从图中删除该顶点,同时删除以他为出度的有向边。
4、重复2、3直到全部顶点均以输出,拓扑有序序列形成;
若图中还有顶点未输出,但是已经跳出处理循环,说明图中还剩下一些顶点,都有直接前驱,再也找不到没有前驱的顶点,此时说明AOV网络中必定存在有向环。

关键路径
关键路径用来估算工程需要完成的时间
AOE网络:定义结点为时间,有向边的指向的指向表示事件执行的次序,有向边定义为活动,权值为所需要的时间。
专用术语
源点:表示整个工程的开始点,也称起点。
收点:表示整个工程的结束点,也称汇点。
事件结点:单位时间表示时刻。
活动(有向边):权值为活动进行所需要的时间,方向表示起始结点事件先发生,终止结点时间才能发生。
事件最早发生时间(Ve(j)):从起点到本结点的最长路径,意味事件最早能够发生的时刻。
ve(1)=0,ve(k) = Max{ ve{j} + Weight(j, k) }
事件最迟发生时间(Vl(j)):不影响工程如期完工,本结点事件必须发生的时刻。
活动最早开始时间(e(ai)):e(ai)=Ve(j)
活动最迟开始时间(l(ai)):l(ai)=Vl(k)-dut(j,k)
关键活动:最早开始时间=最迟开始时间的活动
关键路径:从源点到收点的最长的一条路径,或者全部由关键活动构成的路径

在这里插入图片描述在这里插入图片描述利用拓扑排序求时间结点最早发生时间:
1、设每个结点的最早发生时间为0,将入度为0的结点入栈。
2、将栈中入度为0的结点取出,并压入另外一个栈,用于形成逆向拓扑排序序列。

发生是针对事件的,也就是图中的顶点,只有在指向该顶点的所有有向边的活动结束,改顶点所代表的事件才发生。
开始是针对活动的,也就是图中的边,只有在一个顶点所代表的事件发生后,从顶点出发的所有边对应的活动才能开始。

求目标顶点的最早发生时间:
目标顶点的所有前提顶点的最早完成时间+对应的前提顶点到目标顶点活动中的最大值。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值