MST的Prim算法和Kruskal算法

    最小生成树MST是图论中的基本算法。一般使用Prim算法或者Kruskal算法解决。这两类算法都是贪心算法,Prim算法是基于点的,而Kruskal算法是基于边的。假设无向带权图G(V,E,W),Prim的算法流程大致如下:


令R、Q为顶点的集合,初始R为空集,Q为V
循环:
    从Q中取出点v,满足v到R的距离比Q中其他点都要近,将v加入R
    (所谓v到R的距离,就是v到R中各点的距离最近的那个)
直到Q为空集


    为了实现这个算法流程,还需要记录一个数组D,D记录Q中的节点到R的距离,每一次迭代都有可能更新,初始R为空集,D均设置为无穷大。


    Prim算法的流程就是每一次选择合适的点加入MST,而Kruskal算法则是每次选择合适的边。Kruskal算法需要用到并查集,流程大致如下:


n个节点分成n个部分,即并查集初始化
将边按权值升序排列
循环:对每一条边(u,v)
    如果u、v属于不同部分(并查集find)
        该边属于MST,且并查集合并u和v


    循环中可加入已加入MST的点的数量的判断,有可能提前结束循环,提高效率。


    下面是hdu1233的源代码,一个用Prim算法,另一个用Kruskal,标准的MST问题。

#include <cstdio>
#include <algorithm>
using namespace std;

typedef int weight_t; 

#define SIZE 101

int N;

//图的邻接矩阵
weight_t Graph[SIZE][SIZE];

//各顶点到中间结果的最短距离,始终维护
weight_t D[SIZE];

//标志位
bool Flag[SIZE];

//Prim算法,返回MST的长度
weight_t Prim(){
	//初始化数组
	fill(D,D+SIZE,INT_MAX);
	fill(Flag,Flag+SIZE,false);

	//初始化第一个计算的点
	D[1] = 0;

	weight_t ans = 0;

	for(int i=1;i<=N;++i){
		//找出距离中间结果最近的点
		int k = -1;
		for(int j=1;j<=N;++j)
			if ( !Flag[j] && ( -1 == k || D[j] < D[k] ) )
				k = j;

		//将k点加入中间结果
		Flag[k] = true;
		ans += D[k];

		//更新剩余点到中间结果的最短距离
		for(int j=1;j<=N;++j)
			if ( !Flag[j] && Graph[k][j] < D[j] )
				D[j] = Graph[k][j];
	}

	return ans;
}

bool read(){
	scanf("%d",&N);
	if ( 0 == N ) return false;
	
	for(int i=0;i<N*(N-1)/2;++i){
		int a,b,w;
		scanf("%d%d%d",&a,&b,&w);
		Graph[a][b] = Graph[b][a] = w;
	}
	
	return true;
}

int main(){
	while( read() ){
		printf("%d\n",Prim());
	}
	return 0;
}



#include <cstdio>
#include <algorithm>
using namespace std;

typedef int weight_t; 

#define SIZE 101

//并查集结构
int Father[SIZE];
void init(int n){for(int i=0;i<=n;Father[i]=i++);}
int find(int x){return Father[x]==x?x:Father[x]=find(Father[x]);}
void unite(int x,int y){Father[find(y)]=Father[find(x)];}

int N;

//边结构
struct edge_t{
	int s;
	int e;
	weight_t w;
}Edge[SIZE*SIZE/2];
int ECnt = 0;

//重载,用于边排序
bool operator < (edge_t const&lhs,edge_t const&rhs){
	if ( lhs.w != rhs.w ) return lhs.w < rhs.w;
	if ( lhs.s != rhs.s ) return lhs.s < rhs.s;
	return lhs.e < rhs.e;
}

//生成边
inline void mkEdge(int a,int b,weight_t w){
	if ( a > b ) swap(a,b);

	Edge[ECnt].s = a;
	Edge[ECnt].e = b;
	Edge[ECnt++].w = w;
}

//Kruskal算法,vn是点的数量,en是边的数量,返回MST的长度
weight_t Kruskal(int vn,int en){
	init(vn);//并查集初始化
	sort(Edge,Edge+en);//边排序

	weight_t ans = 0;
	for(int i=0;i<en;++i){
		//该边已存在于MST中
		if ( find(Edge[i].s) == find(Edge[i].e) )
			continue;

		//将该边加入MST
		ans += Edge[i].w;
		unite(Edge[i].s,Edge[i].e);
		--vn;

		//MST已完全生成
		if ( 1 == vn ) break;
	}

	return ans;
}

bool read(){
	scanf("%d",&N);
	if ( 0 == N ) return false;
	
	ECnt = 0;
	for(int i=0;i<N*(N-1)/2;++i){
		int a,b,w;
		scanf("%d%d%d",&a,&b,&w);
		mkEdge(a,b,w);
	}
	
	return true;
}

int main(){
	while( read() ){
		printf("%d\n",Kruskal(N,ECnt));
	}
	return 0;
}



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值