C++代码,数据结构-最小生成树的两个算法,Prime&Kruskal

来源:互联网 发布:软件系统分为 编辑:程序博客网 时间:2024/05/18 02:24
#include<iostream>#include<vector>#include<iterator>#include<queue>using namespace std;//图的数组(邻接矩阵)存储表示,最小生成树,#define INFINITY INT_NAX#define Max_v 20#include<fstream>typedef enum{dg,dn,udg,udn}Graphkind;struct Arccell{int adj;string *info;};typedef Arccell adjmatric[Max_v][Max_v];struct Mgraph{char vexs[Max_v];   //顶点向量adjmatric arcs;   //邻接矩阵int  vexnum,arcnum;  //顶点数 和 弧数Graphkind kind;  // 图的种类标志};int locate(Mgraph G,char ch){for(int i=0;i!=G.vexnum;++i){    if(G.vexs[i]==ch)return i;}}void createUDG(Mgraph &G){//采用数组表示法,构造无向图G    cout<<"Please enter the vexnum and arcnum of the G graph"<<endl;    cin>>G.vexnum>>G.arcnum; cout<<"Please enter the dians of G"<<endl;    for(int i=0;i!=G.vexnum;++i){cin>>G.vexs[i];}//构造顶点向量    for(int i=0;i!=G.vexnum;++i){//初始化矩阵     for(int j=0;j!=G.vexnum;++j){        G.arcs[i][j].adj=10000;        G.arcs[i][j].info=NULL;        }    }char v1,v2;int w;cout<<"Please enter "<<G.arcnum<<" arcnums"<<endl;for(int k=0;k!=G.arcnum;++k){    cin>>v1>>v2>>w;    int i=locate(G,v1); int j=locate(G,v2);    G.arcs[i][j].adj=w; G.arcs[j][i].adj=w;}}void printgraph(Mgraph M){cout<<"\n"<<"The graph has "<<M.vexnum<<"vertex and "<<M.arcnum<<"arc"<<endl;cout<<"The vertex is:";   for(int i=0;i!=M.vexnum;++i){cout<<M.vexs[i]<<" ";}cout<<endl;    for(int i=0;i!=M.vexnum;++i){     for(int j=0;j!=M.vexnum;++j){         if(M.arcs[i][j].adj!=-1)            cout<<M.vexs[i]<<"   "<<M.vexs[j]<<endl;        }    }   for(int i=0;i!=M.vexnum;++i){     for(int j=0;j!=M.vexnum;++j){         cout<<M.arcs[i][j].adj<<"         ";        }cout<<endl;    }}//Prim算法构造最小生成树struct cedge{char adjvex;int lowcost; //权值};typedef cedge closedges[1000];//辅助数组closedges closedge;int minimum(closedges c,int n){     int mi=10000,tem;for(int i=0;i!=n;++i){    if(c[i].lowcost<mi&&c[i].lowcost!=0){        mi=c[i].lowcost;    tem=i;    }}return tem;}void Mini_tree_prime(Mgraph G,char u){//算法7.9. prime 算法int k=locate(G,u);for(int i=0;i!=G.vexnum;++i){    if(i!=k) {        closedge[i].adjvex=u;        closedge[i].lowcost=G.arcs[i][k].adj;    }}closedge[k].lowcost=0;for(int i=1;i<G.vexnum;++i){    k=minimum(closedge,G.vexnum);    cout<<closedge[k].adjvex<<" "<<G.vexs[k]<<endl;    closedge[k].lowcost=0;    for(int j=0;j!=G.vexnum;++j){        if(G.arcs[k][j].adj<closedge[j].lowcost)        {            closedge[j].adjvex=G.vexs[k];            closedge[j].lowcost=G.arcs[k][j].adj;        }    }}}//克鲁斯卡尔算法,利用并查集,边集数组struct edge{int beg;int ends;int weight;};struct parent{//并查集。void initialize()//初始化{    for(int i=0;i!=100;++i){        parents[i]=-1;    }}int finds(int x){//查找x元素所在的集合    if(parents[x]==-1)return x;    else        return parents[x]=finds(parents[x]);}bool merges(int n,int m){ //两个点的所在集合,若相同则返回false;若不同,数字大的集合归到数字小的集合,返回trueif(n==m){    return false;}else if(n<m){    parents[m]=n;return 1;}else{    parents[n]=m; return 1;}}int parents[100];};void mini_tree_Kruskal(Mgraph G)//克鲁斯卡尔算法{edge  edges[G.arcnum];//边集数组parent par;par.initialize();//初始化    int counts=0;for(int i=0;i!=G.vexnum;++i){  //把邻接矩阵转化为边集数组    for(int j=G.vexnum-1;j>i;--j){        if(G.arcs[i][j].adj!=10000){            edges[counts].beg=i;             edges[counts].ends=j;              edges[counts].weight=G.arcs[i][j].adj;              ++counts;        }    }}for(int i=0;i!=G.arcnum;++i){//正确的应该只需要执行到包括所有点即可,但将每条边都进行一遍不影响结果,        int tem,maxx=1000;    for(int  j=0;j!=G.arcnum;++j){        if(edges[j].weight<maxx){            maxx=edges[j].weight;            tem=j;        }    }edges[tem].weight=1001;int m= par.finds(edges[tem].beg);int n= par.finds(edges[tem].ends);if(par.merges(m,n)){//如何不形成回路则输出这条边    cout<<G.vexs[edges[tem].beg]<<"  "<<G.vexs[edges[tem].ends]<<endl;}}}int main(){    ifstream infile("rebuf.txt");cin.rdbuf(infile.rdbuf());   Mgraph m;   createUDG(m);printgraph(m);cout<<endl;Mini_tree_prime(m,'a');cout<<endl;cout<<endl;mini_tree_Kruskal(m);  return 0;}

两个算法在思想理解上 都是相对简单的,但是在转化为代码实现的过程中有一定的难度,

prime算法用到了一个辅助数组,

而Kruskal算法用到了并查集的东西,并查集数据结构书上也没有讲,于是自己网上了查理资料,发现是一种神奇的数据结构, 数据简单但是应用很广泛,自己算是刚刚接触了查并集,对其其他的应用还有待学习,

在Kruskal算法中利用查并集来判断此时加上权值最小的一边是否形成回路,这是kruskal算法中最难的部分,

先用

void initialize()//初始化{    for(int i=0;i!=100;++i){        parents[i]=-1;    }}
使得parents这个并查集初始化,各单点的集合根都为-1 

然后利用

int finds(int x){//查找x元素所在的集合    if(parents[x]==-1)return x;    else        return parents[x]=finds(parents[x]);}
找出点x所在的集合,若x还未属于任何一个集合,即parents[x]=-1,则令其为自己单个元素为集合,

最后通过

bool merges(int n,int m){ //两个点的所在集合,若相同则返回false;若不同,数字大的集合归到数字小的集合,返回trueif(n==m){    return false;}else if(n<m){    parents[m]=n;return 1;}else{    parents[n]=m; return 1;}}
merges判断,若此时边的两点都属于同一个集合,那么返回false;

如果不同则约定集合根数字大的集合接到集合根数字上的上去,并为一个集合,并返回true,输出该边,


0 0
原创粉丝点击