最小生成树算法,应用有哪些,其实很多,例如规划路线费用最小,就是其中的例子,下面以其中2个著名算法为代表编写。
以下算法均以下图为例
Prim普里姆算法
需要遍历顶点,时间复杂度O(n^2),n为顶点数。
lowcost[]数组存放当前生成树到剩余各顶点最短边的权值。
vset[i]指顶点i没有进入树
执行过程:
1.将v0到各边为候选边
2.重复以下步骤n-1次,使n-1顶点进入生成树中
从侯选边选出最小的边输出,并将与该边另一端的顶点v并入生成树
考察剩余顶点vi,如(v,vj)权值小于lowcost[vi],则更新
#include <iostream>
using namespace std;
#define INFINITY 65525
typedef struct{
char vexs[100];
int arc[100][100];
int numVer,numEdges;
}MGraph;
int sum;
void Creat(MGraph &g){
int i,j,k,w;
cout<<"输入顶点数和边数"<<endl;
cin>>g.numVer>>g.numEdges;
for(i=0;i<g.numVer;i++){
cin>>g.vexs[i];
}
for(i=0;i<g.numVer;i++){
for(j=0;j<g.numVer;j++){
g.arc[i][j]=INFINITY;
}
}
cout<<"请输入2个顶点数和权值"<<endl;
for(k=0;k<g.numEdges;k++){
cin>>i>>j>>w;
g.arc[i][j]=w;
g.arc[j][i]=w;
}
}
void Prim(MGraph g){
int lowcost[100],vset[100];
int i,j,k=0,min,v=0;
for(i=1;i<g.numVer;i++){
lowcost[i]=g.arc[0][i];
vset[i]=0;
}
vset[0]=1;//将第一个结点写入树中
sum=0;
for(i=1;i<g.numVer;i++){
min=INFINITY;
//选出最小者
for(j=1;j<g.numVer;j++){
if(vset[j]==0&&lowcost[j]<min){
min=lowcost[j];
k=j;
}
}
vset[k]=1;//写入树
v=k;
sum+=min;
for(j=0;j<g.numVer;j++){
if(vset[j]==0&&lowcost[j]>g.arc[v][j]){
lowcost[j]=g.arc[v][j];
}
}
}
}
int main(){
MGraph g;
Creat(g);
Prim(g);
cout<<sum<<endl;
return 0;
}
以上述图为例,运行结果如下:
Kruskal克鲁斯卡尔算法
思想简单,一般用此方法。
按照边大小排序,然后开始扫描各边,并检测是否为侯选边,即是否会产生回路,如不构成回路则加入生成树中,直到所有边检测完。
#include <iostream>
#include <algorithm>
#define INFINITY 65525
using namespace std;
typedef struct{
int a,b;//连接的两个顶点
int w;
}Road;
Road road[100];
int v[100];
typedef struct{
char vexs[100];
int arc[100][100];
int numVer,numEdges;
}MGraph;
int sum;
void Creat(MGraph &g){
int i,j,k,w;
cout<<"输入顶点数和边数"<<endl;
cin>>g.numVer>>g.numEdges;
for(i=0;i<g.numVer;i++){
cin>>g.vexs[i];
}
for(i=0;i<g.numVer;i++){
for(j=0;j<g.numVer;j++){
g.arc[i][j]=INFINITY;
}
}
cout<<"请输入2个顶点数和权值"<<endl;
for(k=0;k<g.numEdges;k++){
cin>>i>>j>>w;
g.arc[i][j]=w;
g.arc[j][i]=w;
road[k].a=i;
road[k].b=j;
road[k].w=w;
}
}
bool cmp(Road a,Road b){
return a.w<b.w;
}
//在并查集中查找根结点
int getRoot(int a){
while(a!=v[a]) a=v[a];
return a;
}
void Kruskal(MGraph g){
int i,N,E,a,b;
sum=0;
for(i=0;i<g.numVer;i++) v[i]=i;
sort(road,road+g.numEdges,cmp);
for(i=0;i<g.numEdges;i++){
a=getRoot(road[i].a);
b=getRoot(road[i].b);
if(a!=b){
v[a]=b;
sum+=road[i].w;
}
}
}
int main(){
MGraph g;
Creat(g);
Kruskal(g);
cout<<sum<<endl;
return 0;
}
运行结果如下,和Prim算法一致
分析与比较
Prim主要部分是双重循环,只与顶点数有关与边无关,因此适用于稠密图;
Kruskal算法时间主要在sort()和单层循环,排序与边数相关,与顶点数无关,因此适用于稀疏图。
注意:以上算法均针对于无向图。