算法分析与设计——最小生成树Prim算法


一、问题

给定一张带边权的无向图G=(V,E),n=|V|,m=|E|。由V中全部n个顶点和E中n-1条边构成的无向连通子图被称为一颗生成树,而其中边权之和最小的生成树则是该无向图G的最小生成树,应该如何得到一张图G的最小生成树?

二、解析

首先,任意一颗最小生成树一定包含无向图中权值最小的边。

否则通过反证法,最小生成树不含权值最小的边,则可以把这条最小边加到树上,n点n条边必定会构成一个环,把该环上任意另一条边去掉,剩下的仍是联通的生成树,且树的权值总和小于此前的树,则此前的树不满足最小生成树的定义。

Prim算法可以利用这一结论,从一个点开始维护一直维护最小生成树的一部分,并寻找与已经进树的节点相连的未进树的节点构成的边中最小的边,把它加入最小生成树的一部分。

当所有点都联通时,由于保证了每次联通都是剩余边中最小的边,不存在有一条更小的边能替换选中的边的情况,该生成树一定是最小生成树。

三、设计

先确定1号节点默认是最小生成树的一部分

读图,存入点与点之间的边权信息

从1号节点开始扫描,与其相连的点中最短的边和该相连点加入生成树,重复上述步骤

当所有点都被加入之后,便形成了最小生成树

四、分析

由于每一个点都需要遍历加入生成树,且遍历过程中需要遍历剩余每一个点到生成树的最小边,故n个点需要n+(n-1)+(n-2)+……+2+1,为(n+1)*n/2次操作,即时间复杂度为O(n²)

因为要存储所有点两两之间的关系,用邻接矩阵存储的话空间复杂度为O(n²)

五、实例

在这里插入图片描述

六、代码

#include<stdio.h>
#include<stdlib.h>
#include<memory.h>
#include<string.h>
#include<algorithm>

using namespace std;

const int INF = 0x3f3f3f3f;
const int N = 1005;
//a数组存图,d数组维护选出x的最小边值,n是点数,m是边数,ans是最小生成树的边权总和
int a[N][N], d[N], n, m, ans;
//v[x]用来标记x是否已经被加入最小生成树
bool v[N];

void prim() {
	memset(d, INF, sizeof(d));//初始无连接默认为无穷大
	memset(v, 0, sizeof(v));
	d[1] = 0;//加入第一个点,到自己的边值为0,其他值在主函数根据读图更新为到1点最近的距离
	for (int i = 1; i <= n; i++){
		int x = 0;
		for (int j = 1; j <= n; j++) {//用j来遍历不是最小生成树里的点,找到离树最近的那个点的编号赋给x
			if (!v[j] && (x == 0 || d[j] < d[x]))
				x = j;
		}
		v[x] = 1;//x点加入最小生成树
		printf("v%d\n", x);
		for (int y = 1; y <= n; y++){//用y遍历不是最小生成树内的点,更新其到最小生成树的最短距离
			if (!v[y])
				d[y] = min(d[y], a[x][y]);//y点与最小生成树没联通还是无穷大,有联通要比较,可能是原来最短的,也可能是x加入后变短了
		}
	}
}

int main(void){
	scanf("%d%d", &n, &m);
	//构建邻接矩阵
	memset(a, INF, sizeof(a));
	for (int i = 1; i <= n; i++)
		a[i][i] = 0;
	for (int i = 1; i <= m; i++){
		int x, y, z;
		scanf("%d%d%d", &x, &y, &z);
		a[y][x] = a[x][y] = min(a[x][y], z);//考虑有两点间有两条路以上的情况
	}
	//求最小生成树
	printf("加入顺序:\n");
	prim();
	for (int i = 2; i <= n; i++)
		ans += d[i];
	printf("最小生成树边权和为:\n%d\n", ans);
}
/*测试例子
6 10
1 2 6
1 3 1
1 4 5
2 3 5
2 5 3
3 4 5
3 5 6
3 6 4
4 6 2
5 6 6
*/
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值