[问题描述]
若要在n个城市间建立通信联络网,只需要n-1条线路即可。如何以最低的经济代价建设这个通信网,是一个网的最小生成树问题。
[基本要求]
(1)利用图雷姆或克鲁斯卡尔算法求网的最小生成树;
(2)以文本形式输出生成树的各条边及其权值。
[实现提示]
(1)通信线路一旦建立起来一定是双向的,因此构造最小生成树的网一定是无向网;
(2)n个城市间,最多可设置n(n-1)/2条线路;n个城市间建立通信网,只需n-1条线路。问题转化为:如何在可能的线路中选择n-1条,能把所有城市(顶点)均连起来,且总耗费(各边权值之和)最小。
#include <stdio.h> #include <stdlib.h> #define TRUE 1 #define FALSE 0 #define OK 1 #define ERROR 0 #define INFEASIBLE -1 #define OVERFLOW -2 #define INFINITY 99999 #define MAX_VERTEX_NUM 20 //最大顶点个数 typedef int Status; typedef int InfoType; typedef char VertexType; //弧段结点结构 typedef struct ArcNode { int adjvex; //该弧段所指向的顶点的位置 struct ArcNode *nextArc; //指向下一条弧段的指针 InfoType info; //该弧段相关信息 } ArcNode; //顶点表结构 typedef struct VNode { VertexType data; //图结点的数据类型 ArcNode *firstarc; //图结点的头指针,指向依附于该顶点的第一条弧段 }VNode,AdjList[MAX_VERTEX_NUM]; //定义了顶点结点和顶点表类型 //图结构 typedef struct { AdjList vertices; //图的顶点表 int vexnum,arcnum; //图的顶点数和弧数 int kind; //图的种类标志 }ALGraph; //边信息-用于最小生成树算法存储边信息 typedef struct { int startnode; int endnode; InfoType weight; }Edge; //用于Prim最小生成树算法 typedef struct { VertexType adjvex; InfoType lowcost; } Closedge[MAX_VERTEX_NUM]; Closedge closedge; Status PrintElement(ALGraph G,int e) { // 输出元素e的值 printf("%d ",G.vertices[e].data); // 实用时,加上格式串 return OK; } //创建图 Status CreateGraph(ALGraph &G); //销毁图 Status DestroyGraph(ALGraph &G); //获取顶点位置 int LocateVex(ALGraph G, VertexType u); //获取v的第一个邻接顶点 int FirstAdjVex(ALGraph G,VertexType v); //获取返回v的相对于w的下一个邻接顶点 int NextAdjVex(ALGraph G,VertexType v,VertexType w); //添加新顶点v Status InsertVex(ALGraph &G, VertexType v); //删除顶点v Status DeleteVex(ALGraph &G,VertexType v); //添加弧段<v,w> Status InsertArc(ALGraph &G,VertexType v,VertexType w); //删除弧段<v,w> Status DeleteArc(ALGraph &G,VertexType v,VertexType w); Status CreateUDN(ALGraph &G) { int v1,v2,w; int i,j,k; ArcNode *node=NULL; printf("请输入图G的顶点数,边数:\n"); scanf("%d%d",&G.vexnum,&G.arcnum); printf("请输入顶点(整型数):\n"); for(i=0;i<G.vexnum;i++) { scanf("%d",&G.vertices[i].data); G.vertices[i].firstarc=NULL; } for(k=0;k<G.arcnum;k++) { printf("请输入一条弧信息<v1,v2,w>\n"); scanf("%d%d%d",&v1,&v2,&w); i=LocateVex(G,v1); j=LocateVex(G,v2); node=(ArcNode*)malloc(sizeof(ArcNode)); node->adjvex=j; node->info=w; //头插入法 node->nextArc=G.vertices[i].firstarc; G.vertices[i].firstarc=node; } return OK; }//CreateUDN-无向网 //销毁图 Status DestroyGraph(ALGraph &G) { return OK; } //获取顶点位置 int LocateVex(ALGraph G, VertexType u) { int i; for(i=0;i<G.vexnum;i++) { if(G.vertices[i].data==u) return i; } return -1; } //获取v的第一个邻接顶点 int FirstAdjVex(ALGraph G,VertexType v) { ArcNode *p; int i; i=LocateVex(G,v); p=G.vertices[i].firstarc; if(p) return p->adjvex; return -1; } //获取返回v的相对于w的下一个邻接顶点 int NextAdjVex(ALGraph G,VertexType v,VertexType w) { ArcNode *p; int i; i=LocateVex(G,v); p=G.vertices[i].firstarc; while(p) { if(G.vertices[p->adjvex].data==w) { p=p->nextArc; if(p) return p->adjvex; else return -1; } p=p->nextArc; } return -1; } void PrintGraph(ALGraph G) { int i; ArcNode *p=NULL; printf("顶点1(弧尾) 顶点2(弧头) 该该边信息:\n"); for(i=0;i<G.vexnum;i++) { printf("%d",G.vertices[i].data); p=G.vertices[i].firstarc; while(p) { printf("--%d %d ",(p->adjvex)+1,p->info); p=p->nextArc; } printf("\n"); } } int minimum(ALGraph G, Closedge elosedge) { int i,j; int min=INFINITY; for(i=0;i<G.vexnum;i++) { if(elosedge[i].lowcost>0&&min>elosedge[i].lowcost) { min=elosedge[i].lowcost; j=i; } } return j; } //最小生成树——Prim算法 void MiniSpanTree_PRIM(ALGraph G, VertexType u) { // 算法7.9 // 用普里姆算法从第u个顶点出发构造网G的最小生成树T,输出T的各条边。 // 记录从顶点集U到V-U的代价最小的边的辅助数组定义: int i,j,k; ArcNode * p; k = LocateVex(G, u); for ( j=0; j<G.vexnum; ++j ) { // 辅助数组初始化 if (j!=k) { closedge[j].adjvex=u; closedge[j].lowcost=INFINITY; } } //右边的距离值 p=G.vertices[k].firstarc; while(p) { closedge[p->adjvex].lowcost=p->info; p = p->nextArc; } closedge[k].lowcost = 0; // 初始,U={u} for (i=1; i<G.vexnum; ++i) { // 选择其余G.vexnum-1个顶点 k = minimum(G,closedge); // 求出T的下一个结点:第k顶点 // 此时closedge[k].lowcost = // MIN{ closedge[vi].lowcost | closedge[vi].lowcost>0, vi∈V-U } printf("(%d,%d,%d)\n",closedge[k].adjvex, G.vertices[k].data,closedge[k].lowcost); // 输出生成树的边 closedge[k].lowcost = 0; // 第k顶点并入U集 p=G.vertices[k].firstarc; while(p) { if (p->info>0&&p->info < closedge[p->adjvex].lowcost) { // 新顶点并入U后重新选择最小边 // closedge[j] = { G.vexs[k], G.arcs[k][j].adj }; closedge[p->adjvex].adjvex=G.vertices[k].data; closedge[p->adjvex].lowcost=p->info; } p=p->nextArc; } } } // MiniSpanTree //判断边数组中是否有端点为i,j的边 Status HasEdge(Edge *edges,int n,int i,int j) { int k; for(k=0;k<n;k++) { if((edges[k].endnode==i&&edges[k].startnode==j)||(edges[k].startnode==i&&edges[k].endnode==j)) return TRUE; } return FALSE; }
主函数:
void main() { //创建图 ALGraph G; int a,b; CreateUDN(G); printf("\n创建图成功!");PrintGraph(G); printf("最小生成树Prim算法:\n"); printf("输入从哪个顶点开始构建最小生成树:"); scanf("%d",&b); MiniSpanTree_PRIM(G, b); printf("\n"); }