1.任务:
[问题描述]
利用普利姆算法和克鲁斯卡尔算法实现最小生成树问题。
[基本要求]
(1)自行建立图的数据文件,第一行是顶点个数,然后依次是顶点名,接下来是边,用float表示边的权值;
(2)以邻接表或者邻接矩阵表示图皆可;
(3)分别利用prim和Kruskal算法实现最小生成树;
(4)输出最小生成树的权值之和,及所用的边。
2.采用的数据结构
采用图,树,普利姆算法和克鲁斯卡尔算法。
3.算法设计思想
普利姆算法:每一次在还未被选中的顶点中寻找连接已被选中形成树的最小边,直至顶点全部加入最小生成树中。时间复杂度T(n)=O(n^2).
克鲁斯卡尔算法:按照最小的权选择边,当选边不产生回路时,就将这条边选定,直至顶点全部加入最小生成树中。时间复杂度T(n)=O(eloge).
4.源程序:
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
typedef struct VertexType
{
int code; //顶点编号
string info; //顶点其他信息
}VertexType;
typedef struct MGraph
{
int arcs[32][32]; //邻接矩阵
int vexnum,arcnum; //顶点数目,边的数目
VertexType vexs[32]; //顶点信息
int selected[32][32];
bool picked[32];
}MGraph;
void CreateGraph(MGraph &G) //图的建立
{
ifstream infile;
infile.open("最小生成树.txt");
if(infile)
{
infile>>G.vexnum;
for(int i=0;i<G.vexnum;i++)
{
G.vexs[i].code=i;
infile>>G.vexs[i].info;
}
for(int i=0;i<G.vexnum;i++)
{
for(int j=0;j<G.vexnum;j++)
{
infile>>G.arcs[i][j];
G.selected[i][j]=0;
}
G.picked[i]=false;
}
}
else
{
printf("文件打开失败!");
}
infile.close();
}
int FindMin(MGraph &G) //寻找所有边中的最小边
{
int min=256;
int x1,y1;
for(int i=0;i<G.vexnum;i++)
{
for(int j=0;j<G.vexnum;j++)
{
if((G.arcs[i][j]<min)&&(G.arcs[i][j]!=0))
{
min=G.arcs[i][j];
x1=i;
y1=j;
}
}
}
G.selected[x1][y1]=1;
G.picked[x1]=true;
G.picked[y1]=true;
return x1;
}
int FindMinPrim(MGraph &G) //Prim算法,寻找邻接的最小边
{
int min=256;
int x1,y1;
for(int i=0;i<G.vexnum;i++)
{
if(!G.picked[i])
{
for(int j=0;j<G.vexnum;j++)
{
if((G.picked[j]==true)&& (G.arcs[i][j]!=0) && (G.arcs[i][j]<min) )
{
min=G.arcs[i][j];
x1=i;
y1=j;
}
}
}
}
G.selected[x1][y1]=1;
G.picked[x1]=true;
G.picked[y1]=true;
return x1;
}
int Prim(MGraph &G)
{
FindMin(G);
for(int i=0;i<G.vexnum-2;i++)
{
FindMinPrim(G);
}
int sum=0;
for(int i=0;i<G.vexnum;i++)
{
for(int j=0;j<G.vexnum;j++)
{
if(G.selected[i][j])
{
cout<<G.vexs[i].info<<"-"<<G.vexs[j].info<<" (权值为"<<G.arcs[i][j]<<")"<<endl;
sum+=G.arcs[i][j];
}
}
}
return sum;
}
int b[32][32];
int FindKruskal(MGraph &G)
{
int min=256;
int x1,y1;
for(int i=0;i<G.vexnum;i++)
{
for(int j=0;j<G.vexnum;j++)
{
if((G.arcs[i][j]!=0) && (G.arcs[i][j]<min) && (b[i][j]==0))
{
min=G.arcs[i][j];
x1=i;
y1=j;
}
}
}
G.selected[x1][y1]=1;
b[x1][y1]=1;
b[y1][x1]=1;
for(int i=0;i<G.vexnum;i++)
{
if((b[x1][i]==0) && (b[i][y1]==1))
{
b[x1][i]=1;
b[i][x1]=1;
}
if((b[y1][i]==0) && (b[i][x1]==1))
{
b[y1][i]=1;
b[i][y1]=1;
}
}
return x1;
}
int Kruskal(MGraph &G)
{
for(int i=0;i<G.vexnum;i++)
{
for(int j=0;j<G.vexnum;j++)
{
G.selected[i][j]=0;
b[i][j]=0;
}
}
FindMin(G);
int x=FindMin(G);
for(int i=0;i<G.vexnum;i++)
{
if(G.selected[x][i]==1)
{
b[x][i]=1;
b[i][x]=1;
break;
}
}
for(int i=0;i<G.vexnum-2;i++)
{
FindKruskal(G);
}
int sum=0;
for(int i=0;i<G.vexnum;i++)
{
for(int j=0;j<G.vexnum;j++)
{
if(G.selected[i][j])
{
cout<<G.vexs[i].info<<"-"<<G.vexs[j].info<<" (权值为"<<G.arcs[i][j]<<")"<<endl;
sum+=G.arcs[i][j];
}
}
}
return sum;
}
int main()
{
MGraph G;
CreateGraph(G);
int sum=0;
cout<<"用Prim算法实现最小生成树,所用边如下:\n";
sum=Prim(G);
cout<<"最小生成树的权值之和为"<<sum;
cout<<"\n---------------------------------------------\n";
cout<<"用Kruskal算法实现最小生成树,所用边如下:\n";
sum=Kruskal(G);
cout<<"最小生成树的权值之和为"<<sum;
return 0;
}
5.源程序测试数据及结果
最小生成树测试结果
6.存在问题及改进方法
在克鲁斯卡尔算法中,判断选定的边是否构成环时,另外开辟了数组,在每次选定一条边之后就使用类似弗洛伊德算法的方式,对k进行遍历。若顶点i和顶点j不连通,但顶点i与顶点k,顶点k与顶点j连通,则i和j也连通。这种方法在每一次选定边后都要使用三层循环,会导致程序效率较低,可以使用拓扑排序来进行优化。在输出时,边与边之间的关系不是非常明确,可以使用其他方式进行输出。