克鲁斯卡尔算法建立最小生成树

克鲁斯卡尔算法,是每次选出权值最小的边构成最小生成树,选出边时,要避免形成环。最终选出结点数减一条边即可。(避免形成环的问题,采用标号法(并查集),一开始,每个结各自为一个集合,分别给出各自不同大小的标号,选出的边的两端结点的标号将大的那个改成小的,以后选出的边的端点标号不能相同。)

#include<iostream>
#define N 100
int sum=0;//权值和 
using namespace std;
//边节点 
typedef struct vexnode {
	int adjvex;
	char v;
	struct  vexnode *nextarc;
	float info;
}ArNode;
typedef struct Vnode{  //顶点信息 
	char data;
	ArNode *firstarc; //指向第一个边结点 
}Vonde,Adjust[100]; 
typedef struct {
	Adjust survice;//邻接表 
	int vexnum,acrnum;//图的当前顶点数和边数 
}ALgraph;

//邻接表查找
 int locate1(ALgraph&R, char v,int n){//找出字符为v的下标 
 	int j,i;
 	for(i=1;i<=n;i++){
 		if(R.survice[i].data==v){
 			j=i;
		 }
	 }
	 return j;
 } 
void creatUND(ALgraph &R){
	char v1,v2;//顶点的数据值 
	int i1,i2;//输入的两个顶点的位置和权值 
	float i3;//权值 
	printf("请输入顶点数和边数:"); 
	cin>>R.vexnum>>R.acrnum; //输入顶点数和边数 
	printf("请输入各个顶点的值:\n"); 
	for(int i=1;i<=R.vexnum;i++){
		cin>>R.survice[i].data;//输入各个顶点的信息 
		R.survice[i].firstarc=NULL;
		 
	}
   
    printf("请输入每条边依附的两个结点和权值:\n");
	for(int i=1;i<=R.acrnum;i++){
		cin>>v1>>v2>>i3;
		i1=locate1(R,v1,R.vexnum);
		i2=locate1(R,v2,R.vexnum);
		ArNode *p=new ArNode;
		p->info=i3; //记录权值 
		p->v=v1;//记录边点的值 
		p->adjvex=i2;// 记录顶点的值 
		p->nextarc=R.survice[i2].firstarc; //利用头插法把第i1个的顶点插入到第i2个邻接表中 
		R.survice[i2].firstarc=p;//建立的邻接表表示的图为有向图,所以只记录一边,所以下面的部分代码给注释掉了 
	/*	ArNode *p1=new ArNode;
		p1->adjvex=i1;//记录顶点的位置 
		p1->info=i3;//记录权值 
		p1->v=v2;//记录边点的值 
		p1->nextarc=R.survice[i1].firstarc;
		R.survice[i1].firstarc=p1;// 将新节点*p2插入顶点Vi1的邻接表中 */
	} 
	    /* ArNode *p2; //这部分是将邻接表表示的图输出 
         for(int i=1;i<=R.vexnum;i++){
		 p2=R.survice[i].firstarc;
     	while(p2){
			printf("<%c , %c> : %.1f   ",R.survice[i].data,p2->v,p2->info);
			p2=p2->nextarc;
		}
		printf("\n");
}*/ 
}
//协助结构体,用来记录边的起始点,终点,权值信息 
typedef struct end{
	 char start;//边的起点 
	 char end;//变得终点 
	 int  w;//边的权值 
}Edge[100]; 
  
//将边按照权值大小进行排序 
void sort(Edge &E,ALgraph &R){
	char b;
	int num;
	 int m=R.acrnum;
	 for(int i=0;i<m ;i++){
	 	for(int j=0;j<m-i;j++){
	 		if(E[j+1].w<E[j].w){
	 			b=E[j+1].start;
	 			E[j+1].start=E[j].start;
	 			E[j].start=b;
	 			b=E[j+1].end;
	 			E[j+1].end=E[j].end;
	 			E[j].end=b;
	 			num=E[j+1].w;
	 			E[j+1].w=E[j].w;
	 			E[j].w=num;
			 }
		 }
	 }
	
}
//找出两结点的标号最大的那个
int  max(int i,int j){
	if(i>j){
	  	return i;
	}else 
	return j;
} 
//找出两结点的标号的小的那个
int min (int i,int j){
	if(i<j){
	    return i;
	}
	else return j;
} 
void minspantree_Kruskal(ALgraph &G,Edge &E){
	      int u=0; //数组Edge的下标 
	      int parent[G.vexnum+1];//标号用的数组 
	      for(int i=1;i<=G.vexnum;i++){//将他们分成不同的集合 
	      	parent[i]=i;
			  }
			 
	    ArNode *p1; //临结点的指针 
	   for(int i=1;i<=G.vexnum;i++){ //将每条边的起点和终点,权值赋予结构体中EDGE中 
	   	p1=G.survice[i].firstarc; 
	   	while(p1){ 
	   		E[u].start=G.survice[i].data;
	   		E[u].end=p1->v;
	   		E[u].w=p1->info;
	   		u++;
	   		p1=p1->nextarc;
		   }
	   }
	   sort(E,G); //将每条边按照权值的大小排序
	   int n=0;//要挑选出边的下标 
	   for(int i=0;i<=G.vexnum-1;i++){//边的结构体下标是从零开始的 
	   	char head1 =E[n].start;//记录选出边的起始点 
	   	char end1 =E[n].end;//记录选出边的终点 
	   	if(parent[locate1(G,head1,G.vexnum)]!=parent[locate1(G,end1,G.vexnum)]){// 如果结点的标号不相等的话,将权值加上 
	   		  sum+=E[n].w; 
		  int  maxv=max(parent[locate1(G,head1,G.vexnum)],parent[locate1(G,end1,G.vexnum)]);//得到边两端结点标号的最大值 
		  int  minv=min(parent[locate1(G,head1,G.vexnum)],parent[locate1(G,end1,G.vexnum)]);//得到边两端结点标号的最小值 
		     parent[locate1(G,head1,G.vexnum)]=minv;  
			 parent[locate1(G,end1,G.vexnum)]=minv;//他俩选出后成为一个集合,将标号都改为最小值 
		
			 for (int j=1;j<=G.vexnum;j++){//将结点中所有标号都为边两端标号最大值的结点标号都改为最小值 
			 	if(parent[j]==maxv){
			 		  parent[j]=minv;
				 }
			 }
			
		 printf("<%c ,%c> \n",head1,end1); //输出找出变的两个结点 
		
	}
		 n++;
	   } 
	   printf("最小权值之和为:%d",sum); 
	   
	
}
int main (){
	ALgraph R;
	Edge E;//有关边的结构体 
	creatUND(R);
	minspantree_Kruskal(R,E);
	return 0;
} 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值