最小生成树问题(用C/C++实现)

本文介绍了普利姆算法和堆优化版Prim算法,以及克鲁斯卡尔算法,用于在无向图中寻找最小生成树,通过示例展示了如何使用邻接矩阵和并查集数据结构实现。
摘要由CSDN通过智能技术生成

一、普利姆算法Prim

1. 朴素版Prim(O(n2))

集合s:当前连通块中的所有点。

所有点的距离dist[i]初始化为正无穷。

for(i = 0; i < n; i ++)

    找到集合外距离最近的点t。

    用t更新其他点到集合的距离。

    将t放入集合 st[t] = true.


点到集合的距离:该点到集合中点的所有边中长度最小的边。

Step1: 选中1号点,一开始集合内不存在点,故将1号点直接将加入。同时将其他点的距离初始化为正无穷。

Step2: 与1号点相连的是2、3、4号点,更新它们到集合内点的最小距离,此时集合内只有1号点故为与1的距离。

Step3:选中距离最短的,此处是2号点,与2号点相连且不在集合内的是3号,它们的距离为3,3>2故不需要将3号点进行更新。

……以此类推,最终得到将这些节点相连的最短距离的边构成的树即为最小生成树。

#include<iostream>
#include<cstring>
#include<algorithm> 
/*给定一个n个点m条边的无向图,图中可能存在重边和自环,边权可能为负
求最小生成树的树边权重之和,如果最小生成树不存在输出impossible
给定一张边带权的无向图G=(V, E),其中V表示图中点的集合,E表示图中边的集合
n=|V|,m=|E|,由V中全部n个顶点和E中n-1条边构成的无向连通子图被称为G的一棵树,
其中边的权值之和最小的生成树被称为无向图G的最小生成树*/ 
using namespace std;

const int N = 510, INF = 0x3f3f3f3f;
int n, m;
int g[N][N];//稠密图用邻接矩阵 
int dist[N];
bool st[N];

int prim()
{
	int res;
	memset(dist, 0x3f, sizeof dist);
	
	for(int i = 0; i < n; i ++)
	{
		int t = -1;
		for(int j = 1; j <= n; j ++)
		{
			if(!st[j] && (t == -1 || dist[j] < dist[t]))
				t = j;
		}
		
		if(i && dist[t] == INF) return INF;//不是第一个点且到集合中任一点距离都是正无穷,即不连通
		if(i) res += dist[t];//下面t就更新了,所以要在这里累加 
		
		//此处与Dijkstra的区别:min(dist[j], dist[t] + g[t][j]),这里的dist是到起点距离,本算法是到集合距离 
		for(int j = 1; j <= n; j ++) dist[j] = min(dist[j], g[t][j]);
		
		st[t] = true;
		
	}
	return res;
}

int main()
{
	scanf("%d%d", &n, &m);
	
	memset(g, 0x3f, sizeof g);
	
	while(m --)
	{
		int a, b, c;
		scanf("%d%d%d", &a, &b, &c);
		g[a][b] = g[b][a] = min(g[a][b], c);
	}
	
	int t = prim();
	
	if(t == INF) puts("impossible");//不连通,不存在
	else printf("%d\n", t); 
	return 0;
} 

2. 堆优化版的Prim(O(mlogn))

优化思路与堆优化版的Dijkstra一致。

二、克鲁斯卡尔算法Kruskal(O(mlogm))

1. 将所有边按权重从小到大排序  O(mlogm)

2. 枚举每一条边a—b (w = c)

if(a, b不连通) 将这条边加入集合中

#include<iostream>
#include<algorithm>
#include<cstring>
/*给定一个n个点m条边的无向图,图中可能存在重边和自环,边权可能为负
求最小生成树的树边权重之和,如果最小生成树不存在输出impossible
给定一张边带权的无向图G=(V, E),其中V表示图中点的集合,E表示图中边的集合
n=|V|,m=|E|,由V中全部n个顶点和E中n-1条边构成的无向连通子图被称为G的一棵树,
其中边的权值之和最小的生成树被称为无向图G的最小生成树*/ 
using namespace std;

const int N = 200010;
int n, m;
int p[N];
//该算法无需邻接表或邻接矩阵来存图,只需要开一个结构体存每条边即可
struct Edge
{
	int a, b, w;
	//重载小于号,方便比较边权重 
	bool operator< (const Edge &W)const
	{
		return w < W.w;
	}
	
 }edges[N]; 
 
 //并查集 
int find(int x)
{
	if(p[x] != x) p[x] = find(p[x]);
	return p[x];
}
  
int main()
{
	scanf("%d%d", &n, &m);
	for(int i = 0; i < m; i ++)
	{
		int a, b, w; 
		scanf("%d%d%d", &a, &b, &w);
		edges[i] = {a, b, w};
	}
	
	//给所有边排序
	sort(edges, edges + m);
	//一开始n个点有n个集合 
	for(int i = 1; i <= n; i ++) p[i] = i;
	//res存的是边权值和 
	int res = 0, cnt = 0;
	for(int i = 0; i < m; i ++)
	{
		int a = edges[i].a, b = edges[i].b, w = edges[i].w; 
		//找到a, b所在的集合 
		a = find(a), b = find(b);
		if(a != b)
		{
			
			p[a] = b;
			res += w;
			cnt ++;//每遍历一条边,就加一条进去 
		}
	}
	//n个点都要在树中,即n-1条边都要被记录,如果少就是不连通 
	if(cnt < n - 1) puts("impossible");
	else printf("%d\n", res);
	
	
	return 0;
}
 

  • 13
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值