您的位置:首页 > 其它

MST的Prim算法和Kruskal算法

2014-05-29 21:03 363 查看
最小生成树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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: