19 图的最小生成树—Prim算法

 图的最小生成树 Prim算法

要用n-1条边将n个顶点连接起来,那么每个顶点都必须至少有一条边与它相连(要不然这个点就是一个"孤立"的点了)。那就随便选一个顶点开始(反正最终每个顶点都是要选到的),看看这个顶点有哪些边,在它的边中找一个最短的。例如在下面这个图中,首先选择1号顶点,1号顶点有两条边分别是1→2和1→3,其中1→2比较短。“每个顶点都必须至少有一条边与它相连”,那不妨先选择这条最短的边1→2,通过它就将1号顶点和2号顶点连接在一起。这里1号顶点和2号顶点已经被选中。下图中白色的顶点就是被选中的点。

 连接完1号和2号顶点后,那剩下的顶点一定要向这两个顶点靠近,越近越好。于是接下来便开始枚举1号和2号顶点所有的边,看看哪些边可以连接到没有被选中的顶点,并且边越短越好。也就是在1→3、2→3和2→4这三条边中选出最短的一条。这三条边中1→3最短,通过这条边,就可以将3号顶点与1号、2号顶点连接在一起。接下来只需采用刚才的方法继续寻找剩下的顶点中离1、2和3号顶点最近的顶点,也就是继续在1号、2号和3号顶点所有的边中找出一条最短边可以连接到没有被选中的顶点。这次选中的是4号顶点。照此方法,一共重复操作n-1 次,直到将所有顶点都选中,算法结束。这个方法就像是一棵"生成树"在慢慢长大,从1个顶点长到具有n个顶点的树。

 总结:

将图中所有的顶点分为两类:树顶点(已被选入生成树的顶点)和非树顶点(还未被选入生成树的顶点)。首先选择任意一个顶点加入生成树(你可以理解成为生成树的根)。接下来要找出一条边添加到生成树,这需要枚举每一个树顶点到每一个非树顶点所有的边,然后找到最短边加入到生成树。照此方法,重复操作n-1次,直到将所有顶点都加入生成树中。

具体实现步骤:

1. 从任意一个顶点开始构造生成树,假设就从1号顶点,首先将顶点1加入生成树中,用一个一维数组book来标记哪些顶点已经加入了生成树。

2. 用数组 dis记录生成树到各个顶点的距离。最初生成树中只有1号顶点,有直连边时,数组dis中存储的就是1号顶点到该顶点的边的权值,没有直连边的时候就是无穷大,即初始化 dis数组。

3. 从数组dis中选出离生成树最近的顶点(假设这个顶点为j)加入到生成树中(即在数组dis中找最小值)。再以j为中间点,更新生成树到每一个非树顶点的距离(就是松弛),即如果 dis【k】>e【j】【k】则更新 dis【k】=e【j】【k】。

4. 重复第3步,直到生成树中有n个顶点为止。

#include <bits/stdc++.h>
using namespace std;



int edg[10][10];
int m, n;
int book[10];
int _count = 0;
int sum = 0;
int main() {
	clock_t start, finish;

	//读入数据
	cin >> n >> m;
	for (int i = 1; i <= n; i++) {
		for (int j = 1; j <= n; j++) {
			if (i == j)
				edg[i][j] = 0;
			else
				edg[i][j] = 1e6;
		}
		//TODO
	}
	//读入边的信息
	for (int i = 1; i <= m; i++) {
		int x, y, z;
		cin >> x >> y >> z;
		edg[x][y] = z;
		edg[y][x] = z;
	}
	start = clock();
	//初始化
	int dis[10];
	for (int i = 1; i <= n; i++) {
		dis[i] = edg[1][i];
	}
	book[1] = 1, _count++;


	while (_count < n) {
		int min = 1e6;
		int index = 0;
		//找到距离最近的点
		for (int j = 1; j <= n; j++) {
			if (dis[j] < min and book[j] == 0) {
				min = dis[j];
				index = j;
			}
		}
		//选中该点
		book[index] = 1; //加入选定点
		_count++;      //点数加1
		sum += min;    //求和边长
//		cout<<index<<":"<<min<<" ";

		//更新dis的数值
		for (int j = 1; j <= n; j++) {
			if (dis[j] > edg[index][j] and book[j] == 0)
				dis[j] = edg[index][j];
		}
	}

	cout << endl << sum;

	finish = clock();
	cout << endl << "the time cost is:" << double(finish - start) / CLOCKS_PER_SEC << endl;
	return 0;
}

测试数据: 

6 9
2 4 11
3 5 13
4 6 3
5 6 4
2 3 6
4 5 7
1 2 1
3 4 9
1 3 2

测试结果: 19 

从上面介绍的两个最短生成树算法可以看出,如果所有的边权都不相等,那么最小生成树是唯一的。Kruskal算法是一步步地将森林中的树进行合并,而Prim算法则是通过每次增加一条边来建立一棵树。Kruskal算法更适用于稀疏图,Prim算法适用于稠密图。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值