最小(代价)生成树

一个有 n 个结点的连通图的生成树是原图的极小连通子图,且包含原图中的所有 n 个结点,并且有保持图连通的最少的边。最小生成树可以用kruskal(克鲁斯卡尔)算法或prim(普里姆)算法求出。

一.普利姆算法

从图中任意取出一个顶点,把他当作一棵树,然后从这棵树相接的边中选取一条最短(权值最小)的边,并将这条边及其所连接的顶点也并入这棵树中,此时得到一颗有两个顶点的树。然后在这棵树中相连的顶点中选取最短的边,并将图中的所有顶点并入树中为止,此时得到的树就是最小生成树。
流程如图:

/*普利姆算法*/
void Prim(Graph g, int v0, int &sum) {//任意开始节点v0,得到边的总长度
	int lowcost[MAX_NUM], vset[MAX_NUM], v;//邻接节点的最短路径,被访问的标记,最后访问的节点标记
	int i, j, k, min;
	v = v0;
	for (int i = 0; i < g.vetexs; ++i) {
		lowcost[i] = g.arcs[v0][i];
		vset[i] = 0;
	}
	vset[v0] = 1;//将v0并入树中
	sum = 0;//sum清零用来累计树的权值
	for (i = 0; i < g.vetexs; ++i) {
		min = INF;//INF是一个比图中所有边权值都要大的树
		for (j = 0; j < g.vetexs; ++j) //选出当前生成树到其一顶点最短边中的一条
			if (vset[j] == 0 && lowcost[j] < min) {
				min = lowcost[j];
				k = j;
			}
		vset[j] = 1;
		v = k;
		sum += min;//记录最小生成树的总权值,也可以换成其他操作
		/*下面这个循环一刚并入的顶点v为媒介更新候选边*/
		for (j = 0; j < g.vetexs; ++j)
			if (vset[j] == 0 && g.arcs[v][j] < lowcost[j])
				lowcost[j] = g.arcs[v][j];
	}
}


二.克鲁斯卡算法

基本思想:(1)构造一个只含n个顶点,边集为空的子图。若将图中各个顶点看成一棵树的根节点,则它是一个含有n棵树的森林。(2)从网的边集 E 中选取一条权值最小的边,若该条边的两个顶点分属不同的树,则将其加入子图。也就是说,将这两个顶点分别所在的两棵树合成一棵树;反之,若该条边的两个顶点已落在同一棵树上,则不可取,而应该取下一条权值最小的边再试之(3)依次类推,直至森林中只有一棵树,也即子图中含有 n-1条边为止。

大白话:(1)将图中的所有边都去掉。(2)将边按权值从小到大的顺序添加到图中,保证添加的过程中不会形成环(3)重复上一步直到连接所有顶点,此时就生成了最小生成树。这是一种贪心策略。


typedef struct {
	int a, b;//a和b是一条边的两个顶点
	int w;//边的权值
}Road;
Road road[MAX_NUM];
int v[MAX_NUM];//定义并查集数组
int getRoot(int a) {  //在并查集中查找a的根节点的函数
	while (v[a] != -1)a = v[a];//结点a与v[a]是否连接(或者间接即多跳连接)
	//着(在边子集中)同一个结点 ,注意!!此处是循环while而非判断if 
	return a;
}

void Kruskal(Graph g, int &sum, Road *road) {
	int i;
	int N, E, a, b;
	N = g.vetexs;//节点数
	E = g.brim;//边数
	sum = 0;
	for (i = 0; i < g.vetexs; ++i)//初始化并查集
		v[i] = -1;
	sort(road, E);//排序操作,使数组按权值有小到大排序
	for (i = 0; i < E; ++i) {
		a = getRoot(road[i].a);
		b = getRoot(road[i].b);
		if (a != b)//判断结点road[i].a与road[i].b是否连
			//接(或者间接即多跳连接)着(在边子集中)同一个结点,
			//注意:假设结点edges[i].a与结点edges[i].b都跟量
			//外一个结点X相连(或者间接相连),如若不加判断,则三个结点会形成回路 
		{
			v[a] = b;
			sum += road[i].w;//求生成树的权值,这句并不是本算法的固定
			                 //写法,可以换成其他的,,例如输出各边
		}
	}
}


  • 14
    点赞
  • 55
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值