克鲁斯卡尔算法

人生天地间,忽如远来客


思想

今天来写一下最小生成树中的克鲁斯卡尔算法,与其说是生成树的问题 不如说是合并树的问题 初始化的时候将每一个结点看成是一个结点的树,最后通过树的合并变成一颗树,我们是通过边来确定结点 并将其加入到树中,那我们怎么知道结点是不是已经在树中,所以我们也就需要给每一个结点命名,说明它是属于哪一个树, 若是要合成的两个结点是同一个名字则跳过,这种边的选择要进行n-1 次,因为树中结点与边的关系是,结点数等于边的数目加一 ,既然我们是选择边,我们知道图的存储形式中边并没有携带每一个边连接的两个结点, 所以也就需要一个新的结构体来存储边,此结构体必须携带顶点信息,以及边的权值。
请添加图片描述

新的边结构

因为对边排完序之后,其边对应的顶点就遗失了 所以需要重新定义一个边的结构,让边携带顶点信息。

typedef struct{
	int from;//前一个顶点信息 
	int to; //后一个个顶底信息 
	int info;//权值 
}edge;

给结点命名

命名是为了合成树的选结点加入树之前 判断是否结点已经在树中了,要使得两个结点是存在于不同的树的。

	int name[G.vexnum];//存储每一个结点的名字 
	for(int i=0;i<G.vexnum;i++){//初始化这些名字   我们就将其名字命名为其的下标 
		name[i]=i; 
	} 

判断是否构成环

注意:修改名字的时候,两个树合并使用要将另外一个树中的结点名字都要改变 
		int flag1=name[L[i].from];
		int flag2=name[L[i].to];
		if(flag1==flag2){//若是两个顶点的名字相同,就说明此时已经属于同一个树了 
			continue; 
		}
		else{
			num++; sum+=L[i].info;
			//  是两个树合并使用要将另外一个树中的结点名字都要改变 
			for(int j=0;j<G.vexnum;j++){
				if(name[j]==flag2){
					name[j]=flag1;
				}
			}
		}

对边进行排序

因为是结构体中进行排序 所以需要自定义一个cmp

bool cmp(edge form,edge to){
	 return form.info<to.info;
}
sort(L.begin(),L.end(),cmp);//对边进行排序

可执行代码

因为有许多的小细节 比较分散 所以就不分块了

#include<bits/stdc++.h>
using namespace std;
#define MaxInt 32767  //表示极大值∞  其实就是一种无穷标志
#define MVNum  100    //表示最大顶点数 
typedef char VerTexType;//假设顶点的数据结构类型为char 
typedef int  ArcType;//假设权值类型为整形
//下面的是邻接矩阵的 
typedef struct{
	 VerTexType vexs[MVNum];//顶点表 
	 ArcType  arcs[MVNum][MVNum];//邻接矩阵 
	 int vexnum;//图的当前顶点数
	 int arcnum;//图的当前边数
}AMGraph;
typedef struct{
	int from;//前一个顶点信息 
	int to; //后一个个顶底信息 
	int info;//权值 
}edge;
vector<edge> L;
//创建一个无向图 ,其实就是填充两个数组 
void CreateAdjoin(AMGraph &G){
	edge l;
	cout<<"请输入顶点数和边数"<<endl;
	cin>>G.vexnum>>G.arcnum; 
	cout<<"请输入各个顶点名称"<<endl; 
	for(int i=0;i<G.vexnum;i++){
		cin>>G.vexs[i]; 
	}
	for(int h=0;h<G.arcnum;h++){
		char vex1;char vex2;
		cout<<"请输入弧的两个顶点"<<endl;
		cin>>vex1>>vex2;
		//首先来看是否存在这两个顶点  若是存在则找到这两个点对应的下标
		// 当然创建的时候一种更加简单的方式就是遍历二维数组 一个一个输入
		int i=0;while(G.vexs[i++]!=vex1);
		int j=0;while(G.vexs[j++]!=vex2);
		if(i>G.vexnum||j>G.vexnum){//没有找到 
			cout<<"你输入的结点值不正确"<<endl; 
		}
		else{
			cout<<"请输入此边对应的权重"<<endl; 
			cin>>G.arcs[i-1][j-1];
			G.arcs[j-1][i-1]=G.arcs[i-1][j-1];
			l.from=i-1;
			l.to=j-1;
			l.info=G.arcs[i-1][j-1];
			L.push_back(l);
		}
	}
}
/**/ 
bool cmp(edge form,edge to){
	 return form.info<to.info;
}
int Klsker(AMGraph &G){ 
	int name[G.vexnum];//存储每一个结点的名字 
	for(int i=0;i<G.vexnum;i++){//初始化这些名字   我们就将其名字命名为其的下标 
		name[i]=i; 
	} 
	sort(L.begin(),L.end(),cmp);//对边进行排序
	//接下来来进行选边 
	int num=0;int sum=0; 
	for(int i=0;i<G.arcnum;i++){
		if(num==G.vexnum-1) break;
		int flag1=name[L[i].from];
		int flag2=name[L[i].to];
		if(flag1==flag2){//若是两个顶点的名字相同,就说明此时已经属于同一个树了 
			continue; 
		}
		else{
			num++; sum+=L[i].info;
			//将另外一个树中的结点名字都要改变 
			for(int j=0;j<G.vexnum;j++){
				if(name[j]==flag2){
					name[j]=flag1;
				}
			}
		}
	} 
	return sum;
}
int main(){
	AMGraph G;
	CreateAdjoin(G);
	cout<<"此时树的最小的权值之和是"<<Klsker(G); 
}

运行截图

请添加图片描述

请添加图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值