一.问题描述:
在n个城市间建立通信网络,需架设n-1条线路。求解如何以最低经济代价建设此通信网,这是一个最小生成树问题。要求:(1)利用普利姆算法求网的最小生成树;(2)输出生成树中各边及权值。
二.代码实现
#include<stdio.h>
//定义常量#define OK 1
#define MaxInt 5500 //极大值,参考中国最南到最北距离
#define CityNum 200 //最大顶点数,最多城市数量
//使用typedef定义变量 两个意义一个是给变量一个易记且意义明确的新名字,
//另一个是简化一些比较复杂的类型声明
//声明新的类型名来代替原有的类型名typedef char CityNameType;//定义顶点的数据类型为字符型(城市名称类型)
typedef int DistanceType;//假设边的权值类型为整型(城市间的距离类型)
//定义无向网以邻接矩阵储存,C语言结构体//自定义数据类型,将结构体定义成一个数据类型,例如方便以AMGraph.vexnum方式直接定义数据
typedef struct{
CityNameType cityNames[CityNum];//城市名称表,定义char类型数组
DistanceType cityDistance[CityNum][CityNum];//邻接矩阵储存城市间的距离
int citynum;//网的当前点数(城市个数)
}Graph;
//采用邻接矩阵表示法,创建无向网G
int greateCity(Graph &G){
int i=0,j=0,k=0;
//输入城市个数
printf("输入总城市个数:");
scanf("%d",&G.citynum);
if(G.citynum > CityNum){ //城市个数不能超出200,超出请重新输入
printf("输入的城市个数过多,请重新输入\n");
greateCity(G);
}else if(G.citynum < 2){
printf("输入的城市个数小于2,无法建立通信网,请重新输入\n");
greateCity(G);
}else{
//依次输入城市的信息
printf("-------------提示:城市名称为字符类型,例:A、B、C...-------------\n");
for(i = 0; i < G.citynum; i++){
char cityName;//城市名称
fflush(stdin);//清空输入缓冲区,通常是为了确保不影响后面的数据读取
printf("输入第%d个城市的名称:",i+1);
scanf("%c",&cityName);
G.cityNames[i]=cityName;//将输入的城市名称储存到城市名称表
//检验城市名是否重复
for(int v = 0; v < i; v++){
if(cityName == G.cityNames[v]){
printf("城市名称不能重复,请重新输入\n");
i--;//名称重复,回退到上一个城市
}
}
}
//初始化邻接矩阵,城市间的距离均为极大值MaxInt
for(i = 0;i < G.citynum;i++){
for(j = 0; j < G.citynum; j++){
if (i == j) {
G.cityDistance[i][j] = 0;
} else {
G.cityDistance[i][j] = MaxInt;
}
}
}
//构造邻接矩阵
//输入城市间的距离
int length,v1,v2;
printf("---------------------提示:城市间距离为正整数,单位为公里,------------------\n");
printf("---------------------若两城市间无法建设,输入极大值5500------------------\n");
for(v1 = 0; v1 < G.citynum; v1++){
for(v2 = v1+1; v2 < G.citynum; v2++){
printf("输入城市%c和城市%c之间的距离(公里):",G.cityNames[v1],G.cityNames[v2]);
scanf("%d",&length);
if (length <= 0) {
printf("输入的城市间距离为非正整数,请重新输入\n");
v2--;//距离输入错误,回退到上一个城市
} else {
//边<G.cityDistance[v1],G.cityDistance[v2]>的权值为length,即两城市间距离
G.cityDistance[v1][v2]=length;
G.cityDistance[v2][v1]=G.cityDistance[v1][v2];
//确保城市a/b或者b/a显示距离一样
}}
}
}
return OK;
}
//定义辅助数组,记录权值最小的边
struct{
CityNameType adjvex;//最小边在U中的那个顶点(最短城市间距离的在U中的城市名称)
DistanceType lowcost;//最小边上的权值(最短的城市间距离)
}closedge[CityNum];
//获得图顶点在顶点数组中的下标
int LocateVex(Graph G,char v){
int i;
for(i = 0; i < G.citynum; i++){
if(G.cityNames[i] == v){
return i;
}
}
if(i == G.citynum){
return -1;
}
}
//无向网G以邻接矩阵形式储存,从顶点u出发构造G的最小生成树T,输出T的各条边
void miniSpanTree_Prim(Graph G,CityNameType u){
int k = LocateVex(G,u);//k为顶点的下标 K=0
int minLength=0;//记录最短路径的大小
for(int j = 0; j < G.citynum; ++j){//
if (j != k) {
closedge[j].adjvex = u;
closedge[j].lowcost = G.cityDistance[k][j];
//printf("%dd\n",closedge[j].lowcost);
//printf("%dd\n",closedge[j].adjvex);
}
}
closedge[k].lowcost = 0;//初始化,将u点加入U集合
for(int i = 1; i < G.citynum; i++){//选择其余G.vexnum-1个顶点
//k=Min(closedge);//求出下一个顶点k,closedge[k]中存在当前最小边
int min = MaxInt;
k = 0;//下一顶点的下标
for(int t = 0; t < G.citynum; t++){
printf("%d-------",closedge[t].lowcost);
if( closedge[t].lowcost != 0 && closedge[t].lowcost < min){//排除零,查找最小值
min = closedge[t].lowcost;
k=t; //用于查找另一出列结点,转换结点用
}
}
//找k的过程
CityNameType u0 = closedge[k].adjvex;//u0为最小边的一个顶点,u0∈U
CityNameType v0 = G.cityNames[k];//v0为最小边的另一个顶点,v0∈V-U
printf("(%c--%c)",u0,v0);//输出当前最小边(u0,v0)
printf("的权值为%d\n",G.cityDistance[LocateVex(G,u0)][k]);
minLength += G.cityDistance[LocateVex(G,u0)][k];//求最小权值边总和
closedge[k].lowcost = 0;
for(int j = 0; j < G.citynum; j++){
if(G.cityDistance[k][j] < closedge[j].lowcost){
closedge[j].adjvex = G.cityNames[k];//结点转换
closedge[j].lowcost = G.cityDistance[k][j];
}
}
}
printf("\n");
printf("总长度为%d公里\n",minLength);
}
void main(){
int i,j;
Graph G;
greateCity(G);
printf("--------邻接矩阵--------\n");
for(i = 0; i < G.citynum; i++)
{
for(j = 0; j < G.citynum; j++)
printf("%d ",G.cityDistance[i][j]);
printf("\n");
}
printf("铺设线路的最短路线为:\n");
miniSpanTree_Prim(G,G.cityNames[0]);
}
三.代码分析
1.在程序中
(1).首先定义了基本常量城市距离最大值和最大城市数量,在定义城市名称表,城市距离表。
(2).当前城市个数.在采用邻接矩阵表示法,创建无向网G,接着输入总城市个数,不得超过最大城市个数,不得小于最少城市个数,(此处采用了容错处理,出错即返回重新输入),再输入城市名称,且城市名称不得重复,(此处采用了容错处理,名称重复返回重新输入),且采用清空缓冲区处理,防止城市名称输入错误。
(3).初始化邻接矩阵,不同城市距离赋值最大值,相同城市距离为零;接着输入城 市真实距离,且距离只能是正整数,负数即返回重新输入。
2.接着使用普利姆算法求网的最小生成树,先定义辅助数组,记录权值最小的边。
(1).LocateVex方法选择从0节点开始,并经过最小权值边的for循环的选择0节点相关联的最小权值边,将与这条边相关联的另一顶点出列;同时输出最小权值边的相关两结点,且输出此最小权值边的权值(城市距离)。
(2).在出列的节点中相关联的所有边中选择一条不与另一个已出列的节点相关联的权值最小的边,同时输出最小权值边的相关两结点,且输出此最小权值边的权值(城市距离)。
(3).重复进行第2步操作,直到所有的节点和最小权值边出列,同时在进行第2步同时将最小权值边的权值相加,最后输出最优距离。
若是哪里有理解错误的或写错的地方,望各位读者评论或者私信指正,不胜感激。