克鲁斯卡尔算法
克鲁斯卡尔算法的基本思想是以边为主导地位,始终选择当前可用(所选的边不能构成回路)的最小权植边。所以Kruskal算法的第一步是给所有的边按照从小到大的顺序排序。这一步可以直接使用库函数qsort或者sort。接下来从小到大依次考察每一条边(u,v)。
具体实现过程如下:
<1> 设一个有n个顶点的连通网络为G(V,E),最初先构造一个只有n个顶点,没有边的非连通图T={V,空},图中每个顶点自成一格连通分量。
<2> 在E中选择一条具有最小权植的边时,若该边的两个顶点落在不同的连通分量上,则将此边加入到T中;否则,即这条边的两个顶点落到同一连通分量 上,则将此边舍去(此后永不选用这条边),重新选择一条权植最小的边。
<3> 如此重复下去,直到所有顶点在同一连通分量上为止。
其实克鲁斯卡尔算法可以理解为贪心的思想:去找两个顶点去组合成一条权值最小的边。且加入这条边后不能生成回环。这样一直重复到所有顶点都已经有连接边。
#include <bits/stdc++.h>
#include <Time.h>
using namespace std;
/**
定义数据结构体
*/
const int M=100;
typedef char Elemtype;
typedef int ElemInt;
typedef struct Graph{
int n; //顶点
int e; //边
//顶点存储
ElemInt vex[M];
//边存储
ElemInt edges[M][M];
}Graph;
/**
图中边的数据结构定义
*/
typedef struct{
int u; //起点
int v; //终点
int w; //权值
}Edge;
/**
查找顶点边在数组中的位置
*/
int search(ElemInt vex[],ElemInt point){
int i=0;
while(i<M){
if(vex[i]==point) {
return i;
}
i++;
}
return -1;
}
/**
(无向图)邻接矩阵的创建
*/
void CreateGraph(Graph &G){
int l,r;
printf("请输入顶点和边的个数\n");
cin>>G.n>>G.e;
printf("请输入%d条顶点\n",G.n);
for(int i=0;i<G.n;i++)
cin>>G.vex[i];
//初始化矩阵(初始值)
for(int i=0;i<G.n;i++)
for(int j=0;j<G.n;j++)
if(i==j)
G.edges[i][j]=0; //对角线
else
G.edges[i][j]=32767;
printf("请输入%d条边的起始和终点位置\n",G.e);
for(int i=0;i<G.e;i++){
int lpoint,rpoint,power;
printf("请输入第%d条边的信息(起始点 终点 权值)\n",i+1);
cin>>lpoint>>rpoint>>power; //输入边的信息和权值
l=search(G.vex,lpoint);
r=search(G.vex,rpoint);
G.edges[l][r]=power; //l--->r
G.edges[r][l]=power; //r--->l
}
}
/**
排序算法
从小到大
*/
void Sort(Edge E[],int e){
int i,j;
for(i=0;i<e-1;i++)
for(int j=0;j<e-i-1;j++){
if(E[j].w>E[j+1].w){
Edge tmp=E[j];
E[j]=E[j+1];
E[j+1]=tmp;
}
}
}
/**
Kruskal算法求解最小生成树过程
*/
void Kruskal_MinTree(Graph *G){
int i,j,u1,v1,sn1,sn2,k;
int vset[M]; //存放下标对应的顶点所属的连通子图编号
Edge E[M]; //存放G中的所有边
k=0;
//将图转换为边集数组
for(i=0;i<G->n;i++){
for(j=0;j<i+1;j++){
//遍历邻接矩阵,并将存在的权值存入边集数组E[]
if(G->edges[i][j]!=0&&G->edges[i][j]!=32767){
E[k].u=i;
E[k].v=j;
E[k].w=G->edges[i][j];
k++;
}
}
}
//排序
Sort(E,k); //权值升序
for(i=0;i<G->n;i++) //初始化vset[]数组,每个顶点相当于一个连通子图
vset[i]=i;
k=1; //记录已加入生成树中边的数目
j=0; //E中边的下标,从0开始
//寻找合适的边
while(k<G->n){
u1=E[j].u;
v1=E[j].v;
sn1=vset[u1]; //分别得到一条边两个顶点所属的集合编号
sn2=vset[v1];
if(sn1!=sn2){
//两个顶点属于不同集合,该边可以作为最小生成树的一条边
printf("(%d,%d):%d\n",u1,v1,E[j].w);
k++; //边计数加1
for(i=0;i<G->n;i++){
if(vset[i]==sn2) //统一两个集合的编号
vset[i]=sn1;
}
}
j++; //扫描下一条边
}
//cout<<"j:"<<j<<"k:"<<k<<endl; //j从0开始,k从1开始
}
int main(){
Graph G;
CreateGraph(G); //创建矩阵
Kruskal_MinTree(&G); //Kruskal算法生成最小生成树
return 0;
}