您的位置:首页 > 其它

最小生成树两个重要的算法:Prim 和 Kruskal

2012-07-31 20:55 375 查看
最小生成树两个重要的算法:Prim 和 Kruskal。

Prim:时间复杂度O(n^2),适用于边稠密的网络。

Kruskal:时间复杂度为O(e*log(e)),适用于边稀疏的网络。

【Prim主要算法思想和函数】

注:扩展了部分功能,根据需要可以选择得到算法结束时哪些边被选择。

#include <iostream>
using namespace std;

const int N=101;

struct Edge
{
int from;
int to;
};

int matrix

;


/*
最小生成树之Prim算法:
算法思想:选定一点到当前树集合,迭代合并距离当前树最近的点,同时更新剩余的点到当前树的"距离"
n:点的个数;
cost:边的权值,无边用0x3f3f3f3f表示无穷大;(建议初始化cost的时候:memset(cost,0x3f,sizeof(cost));)
edge_arr存放结果选定的边(此功能可选,默认参数为不选)
*/


template <class T>
T MST_Prim(const int & n,T cost[]
,Edge * edge_arr=NULL)
{
int i,j;
T ans=0;
T dis
; //用于记录当前每个点到当前的树的距离
int pre
;
bool vst
={false}; //用于标记点是否在当前树上
for(vst[0]=true,i=0;i<n;i++)
dis[i]=cost[0][i],pre[i]=0;
for(i=1;i<n;i++)
{
int idx=-1;
T Min=0x3f3f3f3f;
for(j=0;j<n;j++)
{
if(! vst[j] && dis[j]<Min)
{
idx=j;
Min=dis[j];
}
}
if(idx==-1)
return -1;
vst[idx]=true;
ans+=dis[idx];
if(edge_arr)
{
edge_arr[i-1].from=pre[idx];
edge_arr[i-1].to=idx;
}
for(j=0;j<n;j++)
{
if(! vst[j] && cost[idx][j]<dis[j])
dis[j]=cost[idx][j],pre[j]=idx;
}
}
return ans;
}

int main()
{
int n;
while(scanf("%d",&n)!=EOF)
{
int i,j;
for(i=0;i<n;i++)
{
for(j=0;j<n;j++)
scanf("%d",&matrix[i][j]);
}
printf("%d\n",MST_Prim(n,matrix));
}
return 0;
}


【Kruskal算法思想和函数】

/*
并查集的一个特性:
用一个数组p[]表示每一个元素的父级元素
最父级的元素的父级元素是一个负数,这个负数的绝对值是这个集合下的元素的个数
*/

template <class T>
bool operator <(const Edge<T> & a,const Edge<T> & b)
{
return a.cost<b.cost;
}

/*
找到x所在集合的最父级代表元素
如果这个集合只有x自己,那么最父级代表元素当然就是它自己
*/
int FindSet(int * p,int x)
{
int tmp,px=x;
while(p[px]>=0) //找到x所在集合的代表元素
px=p[px];
/*
路径压缩,可选,如果需要频繁查询,压缩之后可以提高速度
即把从x到代表元素路径上的所有的元素的父节点都表示为代表元素
*/
while(p[x]>=0)
{
tmp=p[x];
p[x]=px;
x=tmp;
}
return px; //x元素所在集合的代表元素
}

/*
合并x和y所在的集合.
*/
void UnionSet(int * p,int x,int y)
{
int tmp;
x=FindSet(p,x);
y=FindSet(p,y);
if(x==y)
return ;
tmp=p[x]+p[y];
if(p[x]>p[y]) //将小树合并到大树下
{
p[y]=tmp;
p[x]=y;
}
else
{
p[x]=tmp;
p[y]=x;
}
return ;
}


/*
最小生成树算法之Kruskal算法:
算法思想:每次找最小的边,如果在已有的森林中加入该边后会形成回路,则舍弃,否则加入然后合并森林
n:点的个数;
edge_cnt:边的个数
edge[]:保存边的数组
edge_arr:保存选择边的数组,可选功能
*/


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

const int N=1001; //定义能处理的最大点的个数

template <class T>
struct Edge
{
int from;
int to;
T cost;
};

template <class T>
T MST_Kruskal(const int & n,const int & edge_cnt,Edge<T> edge[],Edge<T> * edge_arr=NULL)
{
T ans=0;
int i,x,y,p
,cnt=0;
memset(p,-1,sizeof(p));
sort(edge,edge+edge_cnt);
for(i=0;i<edge_cnt;i++)
{
x=FindSet(p,edge[i].from);
y=FindSet(p,edge[i].to);
if(x!=y)
{
UnionSet(p,x,y);
ans+=edge[i].cost;
if(edge_arr)
edge_arr[cnt]=edge[i];
cnt++;
if(cnt==n-1)
return ans;
}
}
return -1;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: