MST.docx
《MST.docx》由会员分享,可在线阅读,更多相关《MST.docx(8页珍藏版)》请在冰豆网上搜索。
MST
MST
给定一个带权的无向连通图,如何选取一棵生成树,使树上所有边上权的总和为最小,这叫最小生成树.
求最小生成树的算法
(1)克鲁斯卡尔算法
图的存贮结构采用边集数组,且权值相等的边在数组中排列次序可以是任意的.该方法对于边相对比较多的不是很实用,浪费时间.
(2)普里姆算法
图的存贮结构采用邻接矩阵.此方法是按各个顶点连通的步骤进行,需要用一个顶点集合,开始为空集,以后将以连通的顶点陆续加入到集合中,全部顶点加入集合后就得到所需的最小生成树.
下面来具体讲下:
克鲁斯卡尔算法
方法:
将图中边按其权值由小到大的次序顺序选取,若选边后不形成回路,则保留作为一条边,若形成回路则除去.依次选够(n-1)条边,即得最小生成树.(n为顶点数)
第一步:
由边集数组选第一条边
第二步:
选第二条边,即权值为2的边
第三步:
选第三条边,即权值为3的边
第四步:
选第四条边,即权值为4的边
第五步:
选第五条边
普里姆算法
方法:
从指定顶点开始将它加入集合中,然后将集合内的顶点与集合外的顶点所构成的所有边中选取权值最小的一条边作为生成树的边,并将集合外的那个顶点加入到集合中,表示该顶点已连通.再用集合内的顶点与集合外的顶点构成的边中找最小的边,并将相应的顶点加入集合中,如此下去直到全部顶点都加入到集合中,即得最小生成树.
例在下图中从1点出发求出此图的最小生成树,并按生成树的边的顺序将顶点与权值填入表中.
———————>先写出其邻接矩阵
第一步:
从①开始,①进集合,用与集合外所有顶点能构成的边中找最小权值的一条边
①——②权6
①——③权1->取①——③边
①——④权5
第二步:
③进集合,①,③与②,④,⑤,⑥构成的最小边为
①——④权5
③——⑥权4->取③——⑥边
第三步:
⑥进集合,①,③,⑥与②,④,⑤构成的各最小边
①——②权6
③——②权5
⑥——④权2->取⑥——④边
第四步:
④进集合,①,③,⑥,④与②,⑤构成的各最小边
①——②权6
③——②权5->取③——②边
⑥——⑤权6
第四步:
②进集合,①,③,⑥,②,④与⑤构成的各最小边
②——⑤权3->取②——⑤边
这也是在网上找到的一个Kruskal和Prim构造过程图,贴出来:
Prim算法模板
intgraph[arraysize][arraysize];
boolfinal[arraysize];
intd[arraysize];
intmaxData=0x7fffffff;
intN; //结点个数
intprim(intsrc)
{
intsum=0;
intminData;
inti,j;
intv;
memset(final,0,sizeof(final));
for(i=1;i<=N;++i) d[i]=graph[src][i];
d[src]=0; //初始化源点的最短距离为0
final[src]=true;
for(i=1;i {
minData=maxData;
for(j=1;j<=N;++j)
{
if(!
final[j]&&d[j] {
v=j;
minData=d[j];
}
}
final[v]=true;
sum+=d[v];
for(j=1;j<=N;++j)
{
if(!
final[j]&&graph[v][j]graph[v][j])//prim算法仅在此处与Dijkstra算法不同
{
d[j]=graph[v][j];
}
}
}
returnsum;
}
Kruskal算法模板(对边进行操作)
typedefstructedge
{
inta;
intb;
intvalue;
}edge;
edgeedges[earraysize];
intfinal[narraysize]; //存储父节点
intnodecount[narraysize]; //存储该节点孩子结点的个数
boolcmp(edgea,edgeb)
{
returna.value}
intfindp(intn) //寻找父节点
{
if(final[n]==n)
returnn;
else
final[n]=findp(final[n]);
returnfinal[n];
}
boolUnion(intx,inty) //合并
{
introotx=findp(x);
introoty=findp(y);
if(rootx==rooty)
returnfalse;
elseif(nodecount[rootx]<=nodecount[rooty])
{
final[rootx]=rooty;
nodecount[rooty]+=nodecount[rootx];
}
else
{
final[rooty]=rootx;
nodecount[rootx]+=nodecount[rooty];
}
returntrue;
}
intmain()
{
//freopen("1.txt","r",stdin);
intnum=0;
intn,m;
inti,j;
while(scanf("%d%d",&n,&m)!
=EOF)
{
num=0; //记录生成树中的边的数目
for(i=1;i<=m;i++)
{
scanf("%d%d%d",&edges[i].a,&edges[i].b,&edges[i].value);
}
for(i=1;i<=n;i++) //初始化
{
final[i]=i;
nodecount[i]=1;
}
sort(edges+1,edges+m+1,cmp); //排序
for(i=1;i<=m;i++) //遍历所有的边
{
if(Union(edges[i].a,edges[i].b)) //合并
{
num++;
}
if(num==n-1) //找到了最小生成树
break;
}
}
return0;
}
Dijkstra算法:
#include
#include
#include
#include //引入的类,注意如果引入会出现编译错误
#include
//#include
usingnamespacestd;
typedefunsignedlonglongu64_T; //类型定义
constintMAXN=50001; //定义结点个数
constu64_TINF=(u64_T)
(1)<<63; //这种方法比较常用
//constu64_TINF=ULLONG_MAX; //定义unsignedlonglong的最大值
structedge_T{
intv;
u64_Tw;
edge_T*next;
}*adj[MAXN],edges[2*MAXN]; //adj[i]存储以i为起点的邻接表的指针,memo[]存储所有的边
structnode_T{ //用于优先级队列中记录节点的标号和最短路径
intv;
u64_Tlen;
booloperator<(constnode_T&nod)const{//重载优先级队列的操作符,使其从小到大排列(注意格式必须完全一致)
returnlen>nod.len;
}
};
intedgenum; //记录边的总数
u64_Tweight[MAXN];
intvn,en; //记录定点数和边数
u64_Tdist[MAXN]; //存储结点的最小距离
voidaddEdge(intu,intv,u64_Tw){
edge_T*ptr=&edges[edgenum++];
ptr->v=v;
ptr->w=w;
ptr->next=adj[u]; //往前插入边
adj[u]=ptr;
}
voiddijkstra(ints){ //n代表结点总数
priority_queueQ; //使用优先级队列实现
node_Tcur;
cur.v=s;
cur.len=0;
Q.push(cur);
for(inti=1;i<=vn;i++)dist[i]=INF; //除源点以外的所有点的距离设置成无穷大(此处与邻接阵实现不同,临界阵赋值为到源点的距离,这里也可但是麻烦
dist[s]=0;