头文件"Graph.h"
#include<iostream>
#include<algorithm>
using namespace std;
class Edge{
public:
int start;
int end;
int weight;
};
//sort() 第三个参数 从小到大排序
bool cmp(const Edge & a,const Edge & b){
return a.weight<b.weight;
}
class Graph{
public:
Edge *edge;
int vertexNum,edgeNum;
int *root,*next;
Graph(int v,int e){
vertexNum=v;
edgeNum=e;
edge=new Edge [e];
root=new int [vertexNum];
next=new int [vertexNum];
for(int i=0;i<vertexNum;i++){
root[i]=i;
next[i]=i;
}
}
~Graph(){
delete [] edge;
delete [] root;
delete [] next;
}
void setedge(int s,int e,int w,int i){
edge[i].start=s;
edge[i].end=e;
edge[i].weight=w;
}
//root[x]存放x的父顶点,递归求x的父顶点
int findroot(int x){
return root[x]==x?x:findroot(root[x]);
}
bool Union(int v,int u){
int root1=findroot(v);
int root2=findroot(u);
if(root1==root2)//说明v,u在同一个连通分量里,退出
return false;
else{
//v,u所在连通分量的父顶点合并
if(next[root1]>next[root2]){
root[root2]=root1;
next[root1]+=next[root2];
}
else{
root[root1]=root2;
next[root2]+=next[root1];
}
}
return true;
}
int Kruskal(){
int sum=0;
int edgenum=0;
sort(edge,edge+edgeNum,cmp);
for(int i=0;i<edgeNum;i++){
if(Union(edge[i].start,edge[i].end)){
sum+=edge[i].weight;
cout<<"v"<<edge[i].start+1<<"->"<<"v"<<edge[i].end+1<<endl;
edgenum++;
}
//生成树条件顶点数为n,边数为n-1
if(edgenum==vertexNum-1)
break;
}
return sum;
}
};
源文件"main.cpp"
#include<iostream>
#include"Graph.h"
using namespace std;
int main(){
Graph G(6,10);
int result;
G.setedge(0,1,6,0);
G.setedge(0,2,1,1);
G.setedge(0,3,5,2);
G.setedge(1,2,5,3);
G.setedge(1,4,3,4);
G.setedge(2,3,5,5);
G.setedge(2,4,6,6);
G.setedge(2,5,4,7);
G.setedge(3,5,2,8);
G.setedge(4,5,6,9);
result=G.Kruskal();
cout<<result<<endl;
return 0;
}
之前一直在纠结什么时候要写Edge类,什么时候直接用int **edge,对比Prim,个人感觉因为Prim算法利用顶点更新其余顶点的信息,故在遍历时通过对每个顶点的访问,用for(){for()}比较方便,可是Kruskal算法的优点在于时间复杂度与边数相关,如果这时还用二维数组遍历的话时间复杂度就变成O(n^2)了,所以用Edge类来保存边的信息,在访问边的同时可以提取边两端顶点就方便很多啦。
以上观点,仅限个人愚见,非喜勿喷。