数据结构学习:图的最小生成树

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

对于⼀个带权连通⽆向图G = (V, E),⽣成树不同,每棵树的权(即树中所有边上的权值之和)也可能不同。设 R 为 G 的所有⽣成树的集合,若 T 为 R 中边的权值之和最⼩的⽣成树,则 T 称为 G 的最⼩⽣成树

最⼩⽣成树可能有多个,但边的权值之和总是唯⼀且最⼩的
最⼩⽣成树的边数 = 顶点数 - 1。砍掉⼀条则不连通,增加⼀条边则会出现回路

如果⼀个连通图本身就是⼀棵树,则其最⼩⽣成树就是它本身
只有连通图才有⽣成树,⾮连通图只有⽣成森林

Prim 算法(普⾥姆)
从某⼀个顶点开始构建⽣成树;每次将代价最⼩的新顶点纳⼊⽣成树,直到所有顶点都纳⼊为⽌。
时间复杂度:O(|V|^2 )适合⽤于边稠密图

算法实现思路:
1.标记各节点是否已加⼊树
2.计算各节点加⼊树的最低代价
3.找到未加入树的结点中代价最低的加入树,并修改标记
4.重复2和3直至所有结点加入树(每加入一个结点则重新计算各结点加入树的最低代价)

Kruskal 算法(克鲁斯卡尔)
每次选择⼀条权值最⼩的边,使这条边的两头连通(原本已经连通的就不选)直到所有结点都连通
时间复杂度:O( |E| * log2( |E| ) )适合⽤于边稀疏图

算法实现思想:
1.将各条边按权值排序
2.从低到高依次检查每条边的两个顶点是否连通(判断是否属于同⼀个集合),若不连通则连起来,已连通跳过

最短路径问题
⽆权图可以视为⼀种特殊的带权图,只是每条边的权值都为1

// 求顶点u到其他顶点的最短距离(BFS算法宽度优先)
void BFS_MIN_Distance(Grapg G,int u)
{
	for(int i = 0;i<G.vexnum;i++)
	{
		d[i] = 1000000;	//初始化路径长度
		path[i] = -1;	//最短路径从哪个顶点过来
	}
	d[u] = 0;
	visited[u] = True;
	EnQuene(Q,u);
	while(!isEmpty(Q))		//BFS的主过程
	{//当队列Q不为空时
		DeQuene(Q,u);		//队头顶点出队,并返回值为u
		for(w = firstNeighbor(G,u); w >= 0;w = NextNeighbor(G,u,w))
		{
			//定义w为u在图G中的第一个临界点
			//每循环一次,w为除w之外顶点u的下一个邻接点的顶点号,若w是u的最后一个邻接点,则返回-1
			
			if(!visited[w])	//当w为False,即未访问过时
			{
				d[w] = d[u] + 1;	//路径长度加一
				path[w] = u;		//最短路径应为u到w
				visited[w] = True;	//标记w顶点为已访问
				EnQuene(Q,w);		//将w顶点入队
			}
		}
	}
}

最短路径长度在数组d[]中,最短路径在数组path[]中
BFS算法求最短路径的局限性:
BFS算法求单源最短路径只适⽤于⽆权图,或所有边的权值都相同的图

Dijkstra算法

定义三个数组
1.标记各顶点是否已找到最短路径 final
2.最短路径⻓度 dist
3.路径上的前驱 path
思路:
1.从V0 开始,初始化三个数组
2.循环遍历所有结点,找到还没确定最短路径,且dist 最⼩的顶点Vi ,令final[i]=ture
3.检查所有邻接⾃ Vi 的顶点,若其 final 值为false,则更新 dist 和 path 信息
4.循环2和3,直到所有结点都找到最短路径

通过dist[]数组可以知道V0到其他结点的最短路径长度
通过path[]数组可以知道V0到其他结点的最短路径

时间复杂度:O(n^2 )即O(|V|^2)

Dijkstra 算法不适⽤于有负权值的带权图
Eg:
在这里插入图片描述

使用Dijkstra算法的话,第一步从V0到V2,第二步从V0到V1结束。

Floyd算法
求出每⼀对顶点之间的最短路径
使⽤动态规划思想,将问题的求解分为多个阶段

对于n个顶点的图G,求任意⼀对顶点 Vi —> Vj 之间的最短路径可分为如下⼏个阶段:
#初始:不允许在其他顶点中转,最短路径是?
#0:若允许在 V0 中转,最短路径是?
#1:若允许在 V0 、V1 中转,最短路径是?
#2:若允许在 V0 、V1 、V2 中转,最短路径是?

#n-1:若允许在 V0 、V1 、V2 …… Vn-1 中转,最短路径是?

算法借助两个二位数组A[]和path[]
通过使用A[]数组来记录顶点间的最短路径长度
通过使用path[]数组来记录顶点间的最短路径的中转点,若直接到达不需要中转点记为-1

思路:
1.初始,不中转,将两个数组初始化,A[]中记录不中转的最短路径(即一步到达),path[]全部初始化为-1,若不中转无法到达则在数组A[]中记为无穷
2.允许i=1个顶点可以中专,更新两个数组
3.i+1,并重复步骤2,直到 I = n-1

for(int k = 0;k < n;k++)
{
	for(int i = 0;i < n;i++)
	{
		for(int j = 0;j < n;j++)
		{
			if(A[i][j] > A[i][k] + A[k][j])
			{
				A[i][j] = A[i][k] + A[k][j];	//找到更短的路径,更新路径
				path[i][j] = k;					//更新最短路径的中转点
			}
		}
	}
}

时间复杂度,O(|V|^3 )
空间复杂度,O(|V|^2 )

Floyd 算法不能解决带有“负权回路”的图(有负权值的边组成回路),这种图有可能没有最短路径
在这里插入图片描述

有向⽆环图:若⼀个有向图中不存在环,则称为有向⽆环图,简称DAG图
在这里插入图片描述

化简为:
在这里插入图片描述

顶点中不可能出现重复的操作数

Step 1:把各个操作数不重复地排成⼀排
Step 2:标出各个运算符的⽣效顺序(先后顺序有点出⼊⽆所谓)
Step 3:按顺序加⼊运算符,注意“分层”
Step 4:从底向上逐层检查同层的运算符是否可以合体
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

小二康

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

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

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

打赏作者

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

抵扣说明:

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

余额充值