問題描述
Prim和Kruskal經典算法的實現
代碼實現
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#include "windows.h"
const float MAX=100000.0;
typedef struct arc
{ //弧
int index;
float weight;
struct arc *next;
}AR;
typedef struct MyGraph
{ //圖
int type;//0,表示無向網,1表示有向網
int arcnum,vexnum;
char **vexname;
AR *N;
float **A;
}GH;
typedef struct node
{ //Prim節點
int adj;
float weight;
}CL;
typedef struct
{ //Kruskal 節點
int u,v;
float weight;
}Kn;
int *father;//並查集
int findvex(char *s,GH *G);//確定頂點s對應的序號
void creatgraph(GH *G);//以鄰接表的形式創建圖
void showgraph(GH *G);//以鄰接表的形式顯示圖
void MINTR(GH *G,char *s);//最小生成樹 :Prim
int fmin(CL *c,int num);//尋找最小元素
float finddis(GH *G,int st,int ed);//查找兩點距離
int findfather(int x);//查找函數
void generateE(Kn *&E,GH *G);//生成kruskal所需數組
//歸併排序
void merge(Kn *data,int st,int ed,Kn *result);
void sort(Kn* data,int st,int ed,Kn *result);
int kruskal(Kn *E,GH *G);//最小生成樹:Kruskal
int main()
{
GH *G;
int s1,e1,s2,e2;
G=(GH *)malloc(sizeof(GH));
creatgraph(G);//創建圖
printf("【Graph】");
showgraph(G);//顯示圖
printf("【mintr(PRIM)】:\n");
s1=GetTickCount();
MINTR(G,"A");
e1=GetTickCount();
father=(int *)malloc(sizeof(int)*G->vexnum);
Kn *E=(Kn *)malloc(sizeof(Kn)*G->arcnum);
generateE(E,G);
printf("【mintr(Kruskal)】:\n");
s2=GetTickCount();
kruskal(E,G);
e2=GetTickCount() ;
printf("\nTime\n");
printf("Prim:%dms\n",e1-s1);
printf("Kruskal:%dms\n",e2-s2);
return 0;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
int findvex(char *s,GH *G)
{ //確定頂點s對應的序號
int i;
for(i=0;i<G->vexnum;i++)
{
if(strcmp(s,G->vexname[i])==0)
return i;
}
printf("erro\n");
exit(-1);
}
void creatgraph(GH *G)
{//以鄰接矩陣的形式創建圖
FILE *fp;
int i,j,n;
float k;
char s1[20],s2[20];
AR *p;
fp=fopen("graph4.txt","rb");
if(fp==NULL)
{
printf("can not open file\n");
exit(0);
}
fscanf(fp,"%d",&n);
G->vexnum=n;
G->type=0;
G->N=(AR *)malloc(n*sizeof(AR));
G->A=(float **)malloc(sizeof(float *)*n);
G->vexname=(char **)malloc(n*sizeof(char *));
G->arcnum=0;
for(i=0;i<n;i++)
{
fscanf(fp,"%s",s1);
G->vexname[i]=(char *)malloc(strlen(s1)*sizeof(char));
strcpy(G->vexname[i],s1);
G->N[i].next=NULL;
G->A[i]=(float *)malloc(sizeof(float)*n);
for(j=0;j<n;j++) G->A[i][j]=0.0;
}
while(fscanf(fp,"%s%s%f",s1,s2,&k)!=EOF)
{
p=(AR *)malloc(sizeof(AR));
i=findvex(s1,G);
j=findvex(s2,G);
(G->arcnum)++;
p->index=j;
p->weight=k;
p->next=G->N[i].next;
G->N[i].next=p;
G->A[i][j]=k;
if(G->type==0)
{
p=(AR *)malloc(sizeof(AR));
p->index=i;
p->weight=k;
p->next=G->N[j].next;
G->N[j].next=p;
G->A[j][i]=k;
}
}
fclose(fp);//關閉數據文件
}
void showgraph(GH *G)
{ //以鄰接表的形式顯示圖
int i;
AR *p;
for(i=0;i<G->vexnum;i++)
{
printf("\n%s",G->vexname[i]);
p=G->N[i].next;
while(p)
{
printf("--%s(%.2f) ",G->vexname[p->index],p->weight);
p=p->next;
}
}
printf("\n");
}
void MINTR(GH *G,char *s)
{ //最小生成樹:Prim
CL *c=(CL *)malloc(sizeof(CL)*G->vexnum);
int i,j,k;
int st=findvex(s,G);
for(i=0;i<G->vexnum;i++)//初始化
{
c[i].adj=0;
if(i==st) c[i].weight=0;
else
{
AR *p=G->N[i].next;
while(p&&p->index!=st)
{
p=p->next;
}
if(p) c[i].weight=p->weight;
else c[i].weight=MAX;
}
}
for(j=0;j<G->vexnum;j++)
{
k=fmin(c,G->vexnum);
if(strcmp(G->vexname[k],G->vexname[c[k].adj]))
printf("%s %s:%.2f\n",G->vexname[k],G->vexname[c[k].adj],c[k].weight);
c[k].weight=0;
for(i=0;i<G->vexnum;i++)
{
if(finddis(G,k,i)<c[i].weight)
{
c[i].adj=k;
c[i].weight=finddis(G,k,i);
}
}
}
}
float finddis(GH *G,int st,int ed)
{ //查找兩點距離
AR *p=G->N[st].next;
while(p)
{
if(p->index==ed)
return p->weight;
p=p->next;
}
return MAX;
}
int fmin(CL *c,int num)
{//尋找最小元素
int i,k=0;
float min=MAX;
for(i=0;i<num;i++)
{
if(c[i].weight<=min&&c[i].weight>0)
{
min=c[i].weight;
k=i;
}
}
return k;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
int findfather(int x)
{ //查詢部分
int a=x;
while(x!=father[x])
x=father[x];
//路徑壓縮部分
while(a!=father[a])
{
int z=a;
a=father[a];
father[z]=x;
}
return x;
}
void generateE(Kn *&E,GH *G)
{ //生成kruskal所需數組
int arcnum=G->arcnum,vexnum=G->vexnum;
int k=0;
for(int i=0;i<vexnum;i++)
{
for(int j=0;j<i;j++)
{
if(G->A[i][j]>0.0)
{
E[k].u=i;
E[k].v=j;
E[k].weight=G->A[i][j];
k++;
}
}
}
}
void merge(Kn *data,int st,int ed,Kn *result)
{
int Lind=st,Rind=st+(ed-st+1)/2+1;
int L_length=(ed-st+1)/2+1,newind=st;
while(Lind<st+L_length && Rind<ed+1)
{
if(data[Lind].weight<data[Rind].weight)
{
result[newind].weight=data[Lind].weight;
result[newind].u=data[Lind].u;
result[newind].v=data[Lind].v;
newind++; Lind++;
}
else
{
result[newind].weight=data[Rind].weight;
result[newind].u=data[Rind].u;
result[newind].v=data[Rind].v;
newind++;Rind++;
}
}
while(Lind<st+L_length)
{
result[newind].weight=data[Lind].weight;
result[newind].u=data[Lind].u;
result[newind].v=data[Lind].v;
newind++; Lind++;
}
while(Rind<ed+1)
{
result[newind].weight=data[Rind].weight;
result[newind].u=data[Rind].u;
result[newind].v=data[Rind].v;
newind++;Rind++;
}
}
void sort(Kn* data,int st,int ed,Kn *result)
{
if(st==ed) return;
else if(st+1==ed)
{
if(data[st].weight>data[ed].weight)
{
int tempu=data[st].u;
int tempv=data[st].v;
float tempw=data[st].weight;
data[st].u=data[ed].u;
data[st].v=data[ed].v;
data[st].weight=data[ed].weight;
data[ed].u=tempu;
data[ed].v=tempv;
data[ed].weight=tempw;
}
return;
}
else
{
sort(data,st,(ed-st+1)/2+st,result);
sort(data,st+(ed-st+1)/2+1,ed,result);
merge(data,st,ed,result);
for(int i=st;i<=ed;i++)
{
data[i].u=result[i].u;
data[i].v=result[i].v;
data[i].weight=result[i].weight;
}
}
}
int kruskal(Kn *E,GH *G)
{ //最小生成樹:Kruskal
int vexnum=G->vexnum,arcnum=G->arcnum;
int edges=0;
float weightsum=0;
for(int i=0;i<arcnum;i++) father[i]=i;//並查集初始化
Kn *result=(Kn *)malloc(sizeof(Kn)*arcnum);
sort(E,0,arcnum-1,result);
for(int i=0;i<arcnum;i++)
{ //尋找邊的兩個端點所在的樹的父節點
int s1=findfather(E[i].u);
int s2=findfather(E[i].v);
if(s1!=s2)
{
father[s1]=s2;
printf("%s %s:%.2f\n",G->vexname[E[i].u],G->vexname[E[i].v],E[i].weight);
weightsum+=E[i].weight;
edges++;
if(edges==vexnum-1) break;
}
}
if(edges!=vexnum-1)
{
printf("不聯通,無最小生成樹\n");
return -1;
}
else return weightsum;
}
圖
文件形式
結果
(這裏用的圖比較小,看不出來明顯的時間區別,但Prim時間複雜度爲O(N^2),適合用稠密圖,Kruskal時間複雜度爲O(eloge)適用稀疏圖。