最小生成树

本文深入探讨了两种寻找加权无向图最小生成树的经典算法:Prim算法与Kruskal算法。Prim算法从任意顶点开始,逐步构建最小生成树;而Kruskal算法则按边权值排序,通过并查集避免环路,构造最小生成树。文章提供了详细的算法步骤及代码实现。

一:prim算法

该算法以顶点为突破口,在最小生成树集合中找寻具有最小对外边权的顶点。并将该边的另一个不在生成树集合的顶点加入最小生成树集合

/**
最小生成树问题(prim算法) 
输入
5 6
0 1 2
1 2 1
2 3 4
3 4 3
0 4 5
0 3 2
输出
8
**/ 
#include<stdio.h>
#include<iostream>
#define INF 9999999
using namespace std;
int cost[100][100];  //cost[u][v]表示边u,v的权值(不存在的情况下设为INF)
int mincost[100]; //从集合X出发的边到每个顶点的最小权值,,集合X为最小生成树集合,初始化INF 
bool used[100]; //标记顶点是否包含在生成树集合X中,初始化为false 
int V,E;  //V为顶点数,E为边数 
int prim(){
	for(int i=0;i<V;i++){
		mincost[i]=INF;
		used[i]=false; 
	} 
	mincost[0]=0;
	int res=0;
	while(true){
		int v=-1;
		//从不属于X的顶点中选取从X到其权值最小的顶点
		for(int u=0;u<V;u++){
			if(!used[u]&&(v==-1||mincost[u]<mincost[v])){
				v=u;
			}
		}
		if(v==-1){
			break;
		}		 
		used[v]=true;//把顶点v假如集合X 
		res+=mincost[v];  //把边的长度加入结果中 
		for(int u=0;u<V;u++){
			mincost[u]=min(mincost[u],cost[v][u]);  //更新未加入X集合的顶点到集合的权值 
		}
	}
	return res;
} 
int main(){
	int tempx,tempy,co;
	scanf("%d%d",&V,&E);
	//边权初始化为INF
	for(int i=0;i<V;i++)
	for(int j=0;j<V;j++){
		cost[i][j]=INF;
	}
	for(int i=0;i<E;i++){
		scanf("%d%d%d",&tempx,&tempy,&co);
		cost[tempx][tempy]=co;
		cost[tempy][tempx]=co;
	}
	printf("%d\n",prim());
	return 0;
}

二:kruskal算法

该算法按权值将边从从小到大排序,利用并查集原理将边的两个顶点逐渐组建集合,直到将所有顶点加入同一个集合。已经在同一个集合的顶点,不得重复添加

/**
最小生成树问题(kruskal算法) 
输入
5 6
0 1 2
1 2 1
2 3 4
3 4 3
0 4 5
0 3 2
输出
8
**/
#include<stdio.h>
#include<iostream>
#include<algorithm>
using namespace std; 
struct edge{
	int u,v,cost;
};
bool comp(const edge& e1,const edge& e2){
	return e1.cost<e2.cost; 
}
edge es[100]; //存储边的权值 
int V,E; //V为顶点数,E为边数 
int pre[100];
int find(int x)  /*查找祖先节点*/ 
{
	int r=x;
	while(pre[r]!=r)
	{
		r=pre[r];
	}
	int i=x,j;
	while(i!=r)  /*压缩路径*/ 
	{
		j=pre[i];  
		pre[i]=r;
		i=j;
	}
	return r; 
}
//并查集合并 
void join(int x,int y)
{
	int a=find(x);
	int b=find(y);
	if(a!=b)
	{
		pre[a]=b;   
	}
}
//并查集初始化 
void init_union(){
	for(int i=0;i<V;i++){
		pre[i]=i;
	} 
}
int kruskal(){
	sort(es,es+E,comp);
	//并查集初始化
	init_union(); 
	int res=0;
	for(int i=0;i<E;i++){
		edge e=es[i];
		//当边的两点不在同一个集合中时合并集合 
		if(find(e.u)!=find(e.v)){
			join(e.u,e.v);
			res+=e.cost;
		}
	}
	return res;
} 
int main()
{
	int from,to,cost; 
	scanf("%d%d",&V,&E);
	//边数为E但需要2*E个存储无向图 
	//E=E*2;
	for(int i=0;i<E;i=i+1){
		scanf("%d%d%d",&from,&to,&cost);
		es[i].u=from;
		es[i].v=to;
		es[i].cost=cost;
//		es[i+1].v=to;
//		es[i+1].u=from; 
//		es[i+1].cost=cost;
	}
	printf("%d\n",kruskal());
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值