在一个连通网的所有生成树中,各边权值之和最小的生成树成为最小生成树,对于具有n个顶点的连通网来讲,只需要n-1条边便可将所有顶顶点连通起来。多数最小生成树算法利用了简称为MST的性质,主要分为Prim算法和Kruskal算法
MST性质:假设G=(V,E)是一个连通网,U是顶点V的一个非空子集。若(u,v)是一条具有最小权值的边,其中u∈U,v∈V-U,则必存在一棵包含边(u,v)的最小生成树。
Prim算法
又称加点法:从第一个顶点开始,依次连通当前已连通顶点所未接通的权值最小的边对应的顶点,逐步增加点
这是我从博主Rebright-崇明添加链接描述那所参考的代码并进行了优化,通过邻接矩阵存储的图来创建最小生成树
#include<stdio.h>
#define MAX 100
#define INF 0x3f3f3f3f
void Prim(int G[][MAX],int n)
{
int MST[MAX],lowcost[MAX];
int min,minid,sum=0;
for(int i=2;i<=n;i++)
{
lowcost[i]=G[1][i];
MST[i]=1;
}
MST[1]=0;
for(int i=2;i<=n;i++)
{
min=INF;
minid=0;
for(int j=2;j<=n;j++)
{
if(lowcost[j]<min&&lowcost[j]!=0)
{
min=lowcost[j];
minid=j;
}
}
printf("V%d-V%d=%d\n",MST[minid],minid,min);
sum+=min;
lowcost[minid]=0;
for(int j=2;j<=n;j++)
{
if(G[minid][j]<lowcost[j])
{
lowcost[j]=G[minid][j];
MST[j]=minid;
}
}
}
printf("The minum sum of the total cost is:%d\n",sum);
}
int main()
{
int x,y,m,n,i,j,l,cost;
int G[MAX][MAX];
printf("Please input the number of the vertex:");
scanf("%d",&m);
printf("Please input the number of the edge:");
scanf("%d",&n);
for(i=1;i<=m;i++)
{
for(j=1;j<=m;j++)
{
G[i][j]=INF;
}
}
for(l=1;l<=n;l++)
{
printf("Please input the start:");
scanf("%d",&i);
printf("Please input the end:");
scanf("%d",&j);
printf("Please input the cost:");
scanf("%d",&cost);
G[i][j]=cost;
G[j][i]=cost;
}
Prim(G,m);
return 0;
}
Prim算法的时间复杂度为O(n^2),与网中的边数无关,适合于稠密图
Kruskal算法
又称加边法:通过将边按权值从小到大排序,依次按照当前最小边来合并连通分量,逐步增加边,避免环的出现
同样是参考过来的的代码进行了优化,一样利用邻接矩阵存储的图创建最小生成树
#include<stdio.h>
#define MAXE 100
#define MAXV 100
typedef struct
{
int vex1;
int vex2;
int value;
}Edge;
void Kruskal(Edge E[],int n,int e)
{
int i,j,m1,m2,sn1,sn2,k,sum=0;
int vset[n+1];
for(i=1;i<=n;i++)
{
vset[i]=i;
}
k=1;
j=0;
while(k<e)
{
m1=E[j].vex1;
m2=E[j].vex2;
sn1=vset[m1];
sn2=vset[m2];
if(sn1!=sn2)
{
printf("V%d-V%d=%d\n",m1,m2,E[j].value);
sum+=E[j].value;
k++;
if(k>=n)
{
break;
}
for(i=1;i<=n;i++)
{
if(vset[i]==sn2)
{
vset[i]=sn1;
}
}
}
j++;
}
printf("The minum total cost is:%d\n",sum);
}
void QuickSort(Edge *A,int L,int R)
{
Edge t,temp;
int i,j;
if(L>R)
{
return;
}
temp=A[L];
i=L;
j=R;
while(i!=j)
{
while(A[j].value>=temp.value&&i<j)
{
j--;
}
while(A[i].value<=temp.value&&i<j)
{
i++;
}
if(i<j)
{
t=A[i];
A[i]=A[j];
A[j]=t;
}
}
A[L]=A[i];
A[i]=temp;
QuickSort(A,L,i-1);
QuickSort(A,i+1,R);
}
int main()
{
Edge E[MAXE];
int nume,numn;
printf("Please input the number of the vertex:");
scanf("%d",&numn);
printf("Please input the number of the edge:");
scanf("%d",&nume);
for(int i=0;i<nume;i++)
{
printf("Please input the start:");
scanf("%d",&E[i].vex1);
printf("Please input the end:");
scanf("%d",&E[i].vex2);
printf("Please input the cost:");
scanf("%d",&E[i].value);
}
QuickSort(E,0,nume-1);
Kruskal(E,numn,nume);
return 0;
}
而Kruskal的算法复杂度为O(elog e),与网中的边数e有关,适合于稀疏图。
该代码比较直观明了,但我觉得美中不足的是这是利用了其他存储结构来表示最小生成树,而我所期望的是直接改变图的结构来实现最小生成树,或许是我多虑了。。。