数据结构——最小生成树
数据结构课上讲的最小生成树思路还要代码和我之前写过的ACM版的是一样的,这里都是两种算法普里姆(Prim)算法和克鲁兹卡尔(Kruskal)算法。
https://www.cnblogs.com/wkfvawl/p/9140591.html
普利姆算法
struct { int adjvex; ///保存邻接顶点下标的数组 int lowcost;///记录当前生成树到剩余顶点的最小权值 } closedge[n]; int sum=0;///最小生成树的权值 int Minimum(int closedge[], MGraph G)///求依附于生成树上顶点中所有边代价最下的边 { int j,p=1, min=999;///最大权值65535? for(j=0; j<G.vexnum; j++) { if (closedge[j].lowcost!=0&&closedge[j].lowcost<min) { ///不在生成树中的顶点而且权值最小 min=closedge[j].lowcost;///更新 p=j; } } sum=sum+min;///将该边的权值加入到最小生成树的总权值中 return p; /*返回最小代价的边所依附的生成树外的顶点p*/ }/* Minimum*/ void MiniSpanTree_PRIM(MGraph G, int n,char u) { /*用Prim算法从第u个顶点出发构造网G的最小生成树T,并输出T的各条边, closedge [n] 记录从顶点集U到V-U的代价最小的边,n为图中顶点数*/ int i,j,k; k=LocateVex(G,u); /*求顶点u在邻接矩阵存储的图中的位置*/ for(j=0; j<G.vexnum; ++j) /* 辅助数组初始化*/ { if(j!=k) { closedge[j].adjvex=u; closedge[j].lowcost=G.arcs[k][j].adj; } } closedge[k].lowcost=0; /*初始,U={u}*/ printf("最小代价生成树的各条边为:\n"); for(i=1; i<G.vexnum; ++i) /*选择其余G.vexnum-1个顶点*/ { k=Minimum(closedge,G); /*求出T的下个顶点,第k个顶点*/ printf("%c-%c)\n",closedge[k].adjvex,G.vexs[k]);///打印权值最小的边 /* 输出生成树的边及权值*/ closedge[k].lowcost=0; /*第k个顶点并入U集*/ for(j=0; j<G.vexnum; ++j) { if(closedge[j].lowcost!=0&&G.arcs[k][j].adj<closedge[j].lowcost)/// { ///如果新加入树的顶点k使得权值变小 /* 新顶点并入U集后重新选择最小边*/ closedge[j].adjvex=G.vexs[k]);///修改这条边邻接的顶点,表示这条边是从选出的顶点k指过来的 closedge[j].lowcost=G.arcs[k][j].adj;///更新更小的权值 } } } }
说明
1.这个代码多一个adjvex数组,主要是为了打印最小生成树的边服务的,它用来保存邻接顶点的下标。
2.lowcost数组记录当前生成树到其余顶点的最小权值。lowcost[i]=0表示i号顶点在生成树中了,换句话说,这个lowcost数组既有了dist数组的功能,又有了vist数组的功能。
3.adjvex数组和lowcost数组是同步更新的。一旦有新的点加入到集合中,若找到权值最小的边,同时更新adjvex数组和lowcost。
4.数组结构课更看重求解最小生成树的过程,因而需要去打印组成边,而ACM比赛更看重那个结果,也就最小生成树的权值,这里我又加上了sum,代表最小生成树的权值。
克鲁斯卡尔算法
typedef struct { int a,b;///边的两个结点 int weight;///边的权值 } Edge; ///边结构体 int Find(int *parent,int x) { while(parent[x]!=0)///循环向上寻找下标为x顶点的根 { x=parent[x]; } return x;///循环结束时找到了根的下标 } int parent[MAXVEX];///定义生成树的父节点(并查集) Edge edges[MAXVEX]; ///定义边集数组 void MiniSpanTree_Kruskal(MGraph G) { int i,n,m; sort(edges);///按权值从小到大对边排序 for (i= 0; i <G.vexnum; i++) { parent[i]=0;///初始化各个顶点单独形成一个集合 } for (i=0;i<G.arcnum;i++) { n = Find(parent, edges[i].a);///n是这条边第一个顶点的根节点所在的下标 m = Find(parent, edges[i].b);///n是这条边第二个顶点的根节点所在的下标 if (n!=m) ///根节点下标不同,就说不在一棵树上,不会形成环 { parent[n] = m;///合并 printf("(%d,%d) %d ", edges[i].a, edges[i].b, edges[i].weight); } } }
这个就和之前的写法基本一致了,并查集加排序