數據結構實戰(6) 最小生成樹

問題描述

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)適用稀疏圖。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章