人生天地间,忽如远来客
思想
今天来写一下最小生成树中的克鲁斯卡尔算法,与其说是生成树的问题 不如说是合并树的问题 初始化的时候将每一个结点看成是一个结点的树,最后通过树的合并变成一颗树,我们是通过边来确定结点 并将其加入到树中,那我们怎么知道结点是不是已经在树中,所以我们也就需要给每一个结点命名,说明它是属于哪一个树, 若是要合成的两个结点是同一个名字则跳过,这种边的选择要进行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);
}