③若终点t被设为已扫描点,则搜索结束。
由t开始遍历后向顶点指针p直至起点s,即获得最小费用路径解。
2、数据结构
对于无向网络,采用邻接表的结构存储。
①抽象数据类型
ADTR[N]{
数据对象:
D={
|
int,i=1,2,3....}
数据关系:
R=
}}ADTR[N]
定义一个数组a[],存储任意两点之间的权值。
定义一个数组b[],每个数组元素表示当前所找到的从始点vi到vk最小费用的值。
它的初态为:
若从vi到vj有边,则为边的权值;否则置为∞。
定义一个数组fee[],其元素fee[k]用以记录vi到vk最短路径中vk的直接前驱结点的序号,如果vi到vk存在边,则fee[k]初值为i。
定义一个数组tag[],记录某个顶点是否已计算过最小费用,如果tag[k]=0,则vk
V-S,否则vk
S。
初始值除tag[i]=1外,所有值均为0。
②边表结点结构体
EdgeNode//边表结点
{
定义一个访问标记boolflag;
顶点域charname;
顶点所能达到的结点chars_name;
边上信息doublefee;
结点的指针域EdgeNode*next;
};
③顶点表结构体
Vnode//顶点表结点
{
顶点域charvname;
边表的头指针EdgeNode*firstedge;
};
④无向网图的结构体
Algraph
{
邻接表类型的数组VnodeAdjList[MaxVerNum];
邻接表的顶点数与边数intn,e;
};
⑤自定义函数
voidCreate_Algraph(Algraph*G)//创建无向网图
{
根据边及顶点的个数
for()
{申请空间,并将相应的信息填入空间中}
}
voidPrint_Algraph(Algraph*G);//以邻接表的存储形式打印出网络的信息
voidChange_Flag(Algraph*G,charname)
{若结点被访问,就将flag修改为1;}
EdgeNode*Min_Fee(Algraph*G,charname)//求去最小费用的函数
{
对于一个顶点,若与其相连的顶点的费用,都被访问过(即flag=1)
那么就返回该结点的上一级结点
如果至少有一条路径没被访问,那么就将最小费用的结点返回;
}
voidMin_Tree(Algraph*G,Vnode*V,charname)/*求解最小生成树,name为
最小生成树的根节点*/
{
根据给出的根结点,并调用Min_Fee()函数,求取最小费用,在与其他结点的最小费用相比,在求出最小费用;
将最小费用的结点用链表的形式存储;
}
voidPrint_MinTree(EdgeNode*N);//打印最小生成树
doubleTotal_Fee(Vnode*V);//计算最小生成树对应的费用
⑥本程序的保护模块:
结构体模块
主程序模块
自定义函数模块
调用关系:
3、程序设计
主要算法的程序流程图:
Create_Algraph()的算法流程图:
Print_Algraph()的算法流程图:
Change_Flag()的算法流程图:
Min_Fee()的算法流程图:
Min_Tree()的算法流程图:
Print_MinTree()的算法流程图:
Total_Fee()的算法流程图:
四、详细设计
1、结构体的定义、全局变量及宏定义
#defineMaxVerNum9
#defineEdgeNodestructnode
#defineVnodestructvnode
#defineAlgraphstructalgraph
#defineM9//网络的顶点数
#defineE15//网络的边数
doublefee[15]={32.8,44.6,12.1,18.2,5.9,21.3,41.1,56.4,67.3,
98.7,85.6,10.5,79.2,52.5,8.7};
chara[15]={'A','A','A','A','B','C','C','C','D','D','E','E','F','G','H'};
charb[15]={'B','C','H','I','C','D','E','G','E','F','F','G','I','H','I'};
/*定义全局变量,这三个数组是将网络信息录入,不用手动输入,当网络有所变化时,只需将数组内部的内容改动即可*/
//注意:
这里节点名称要以大写字母表示,且从A开始
2、自定义函数
voidCreate_Algraph(Algraph*G)//创建无向网图
{inti;
EdgeNode*s,*t;
G->n=M;
G->e=E;
for(i=0;in;i++)
{
G->AdjList[i].vname=(char)(i+65);
G->AdjList[i].firstedge=NULL;
}
for(i=0;ie;i++)
{
s=(EdgeNode*)malloc(sizeof(EdgeNode));
t=(EdgeNode*)malloc(sizeof(EdgeNode));
s->name=a[i];
s->fee=fee[i];
s->flag=0;
s->next=G->AdjList[(int)b[i]-65].firstedge;
G->AdjList[(int)b[i]-65].firstedge=s;
t->name=b[i];
t->fee=fee[i];
t->flag=0;
t->next=G->AdjList[(int)a[i]-65].firstedge;
G->AdjList[(int)a[i]-65].firstedge=t;
}
//下面是手动输入网络信息的代码
/*inti;
chara,b;
floatf;
EdgeNode*s,*t;
for(i=0;i<9;i++)
{
G->AdjList[i].vname=(char)(i+65);
G->AdjList[i].firstedge=NULL;
}
cout<<"请输入小区的个数:
"<cin>>G->n;
cout<<"可进行管道铺设的边数:
"<cin>>G->e;
cout<<"请输入小区代号(输入格式为:
小区号):
"<for(i=0;in;i++)
{
cin>>G->AdjList[i].vname;
G->AdjList[i].firstedge=NULL;
}
cout<<"请输入两个小区之间铺设管道的费用(输入格式:
A(空格)B(空格)费用):
"<for(i=0;ie;i++)
{
cin>>a>>b>>f;
s=(EdgeNode*)malloc(sizeof(EdgeNode));
t=(EdgeNode*)malloc(sizeof(EdgeNode));
s->name=b;
s->fee=f;
s->flag=0;
s->next=G->AdjList[(int)a-65].firstedge;
G->AdjList[(int)a-65].firstedge=s;
t->name=a;
t->fee=f;
t->flag=0;
t->next=G->AdjList[(int)b-65].firstedge;
G->AdjList[(int)b-65].firstedge=t;
}*/
}
voidPrint_Algraph(Algraph*G)//以邻接表的形式打印出网络的信息
{inti;
EdgeNode*s;
for(i=0;in;i++)
{
s=G->AdjList[i].firstedge;
cout<AdjList[i].vname<<"\t-->"<<'\t';
while(s)
{
cout<name<<"("<fee<<")"<<'\t';
s=s->next;
}
cout<}
}
voidChange_Flag(Algraph*G,charname)//若结点被访问,就将flag修改为1;
{EdgeNode*N;
inti;
for(i=0;in;i++)
{
N=G->AdjList[i].firstedge;
while(N)
{
if(N->name==name)
{
N->flag=1;
break;
}
N=N->next;
}
}
}
EdgeNode*Min_Fee(Algraph*G,charname)
{//返回某个结点对应的最小费用的结点
EdgeNode*N,*s;
inti;
for(i=0;in;i++)
{
if(G->AdjList[i].vname==name)
break;
}
N=G->AdjList[i].firstedge;
while(N->flag&&N->next)
N=N->next;
s=N;
while(N)
{
if(N->feefee&&N->flag==0)
s=N;
N=N->next;
}
returns;
}
voidMin_Tree(Algraph*G,Vnode*V,charname)/*求解最小生成树,name为最小生成树的根节点*/
{
EdgeNode*start,*s,*t,*p,*q,*N;
inti;
for(i=0;in;i++)
{
if(G->AdjList[i].vname==name)
break;
}
start=(EdgeNode*)malloc(sizeof(EdgeNode));
start->name=name;
start->s_name=name;
start->fee=0;
start->next=V->firstedge;
V->firstedge=start;
Change_Flag(G,name);
for(i=0;in-1;i++)
{
t=V->firstedge;
if(Min_Fee(G,t->name)->flag==1)
{/*若果Min_Fee返回的节点对应的邻接表中结点均被访问过,
那么就在最小生成树的结点表中找下一个结点*/
while(Min_Fee(G,t->name)->flag==1)
t=t->next;
}
s=t;
while(t)
{
p=Min_Fee(G,s->name);
if((p->fee)>(q->fee)&&q->flag==0&&p->flag==0)
s=t;
t=t->next;
}
N=(EdgeNode*)malloc(sizeof(EdgeNode));
N->s_name=s->name;
N->name=Min_Fee(G,s->name)->name;
N->fee=Min_Fee(G,s->name)->fee;
N->next=V->firstedge;
V->firstedge=N;
Change_Flag(G,N->name);
}
}
voidPrint_MinTree(EdgeNode*N)//打印最小生成树
{
if(N->next)
{
Print_MinTree(N->next);
cout<s_name<<"--("<fee<<")--"<name<}
}
doubleTotal_Fee(Vnode*V)
{//计算最小费用
EdgeNode*N;
N=V->firstedge;
doublesum=0;
while(N->next)
{
sum+=N->fee;
N=N->next;
}
returnsum;
}
3、主函数
intmain()
{
charstart_name;
Vnode*V;
Algraph*G;
V=(Vnode*)malloc(sizeof(Vnode));
V->firstedge=NULL;
G=(Algraph*)malloc(sizeof(Algraph));
cout<cout<Create_Algraph(G);
cout<<"各个小区之间可供选择的铺设路径及费用如下:
"<Print_Algraph(G);
cout<<"请输入起始点的小区号:
"<cin>>start_name;
Min_Tree(G,V,start_name);
cout<<"铺设管道的最佳路径为:
"<Print_MinTree(V->firstedge);
cout<<"最佳路径的总费用为:
"<return0;
}
4、函数的调用关系
主函数main()调用自定义函数voidCreate_Algraph(Algraph*G)/*创建邻接表*/,voidPrint_Algraph(Algraph*G)/*以邻接表的形式打印出网络的信息*/,voidMin_Tree(Algraph*G,Vnode*V,charname);/*求解最小生成树,name为最小生成树的根节点*/,voidPrint_MinTree(EdgeNode*N);/*打印最小生成树*/,doubleTotal_Fee(Vnode*V)等等。
voidMin_Tree(Algraph*G,Vnode*V,charname);/*求解最小生成树,name为最小生成树的根节点*/调用了voidChange_Flag(Algraph*G,charname);/*若结点被访问,就将flag修改为1*/;EdgeNode*Min_Fee(Algraph*G,charname);/*求结点的最小费用*/等等。
五、调试分析
1、为了方便调试,所以将小区间管道铺设的信息当做全局变量放在程序中,直接输出,省去每次调试都要输入很多信息。
2、在调试的过程中,会遇到很多问题,主要是因为邻接表的结构有点复杂,操作起来容易出错,查找错误的过程就很困难,但是,通过耐心的调试,还是得到了最终的结果。
3、算法的时空分析
无向网的顶点表是一个数组,这个数组的每个元素有一个数据域和一个指针域,其指针域的指向是一个链表。
所以,在查找顶点域中的数据时,时空复杂度较低,在查找链表中的数据时,就略有些麻烦。
4、该编写的程序存在的缺陷是:
小区的顶点只能以大写字母来表示,而且必须从A开始,依次向下排,不具有普适性。
六、使用说明
1、该程序将无向网的信息当做全局变量,避免多次调试引起多次大量数据的输入,对于小区的名称,必须用大写字母来表示,而且必须从A开始,依次向下排。
2、用户根据提示输入最小生成树的根结点。
3、程序将自动给出最小生成树的边,及最佳方案的总费用。
七、结果
1、以A为最小生成树的根结点的调试结果:
2、以C为最小生成树的根结点
八、附录
源程序清单:
#defineMaxVerNum9
#defineEdgeNodestructnode
#defineVnodestructvnode
#defineAlgraphstructalgraph
#defineM9//网络的顶点数
#defineE15//网络的边数
doublefee[15]={32.8,44.6,12.1,18.2,5.9,21.3,41.1,56.4,67.3,
98.7,85.6,10.5,79.2,52.5,8.7};
chara[15]={'A','A','A','A','B','C','C','C','D','D','E','E','F','G','H'};
charb[15]={'B','C','H','I','C','D','E','G','E','F','F','G','I','H','I'};
/*定义全局变量,这三个数组是将网络信息录入,不用手动输入,当网络有所变化时,只需将数组内部的内容改动即可*/
//注意:
这里节点名称要以大写字母表示,且从A开始
voidCreate_Algraph(Algraph*G);//创建邻接表
voidPrint_Algraph(Algraph*G);//以邻接表的形式打印出网络的信息
voidChange_Flag(Algraph*G,charname);//若结点被访问,就将flag修改为1;
EdgeNode*Min_Fee(Algraph*G,charname);
voidMin_Tree(Algraph*G,Vnode*V,charname);//求解最小生成树,name为最小生成树的根节点
voidPrint_MinTree(EdgeNode*N);//打印最小生成树
doubleTotal_Fee(Vnode*V);
intmain()
{
charstart_name;
Vnode*V;
Algraph*G;
V=(Vnode*)malloc(sizeof(Vnode));
V->firstedge=NULL;
G=(Algraph*)malloc(sizeof(Algraph));
cout<cout<Create_Algraph(G);
cout<<"各个小区之间可供选择的铺设路径及费用如下:
"<Print_Algraph(G);
cout<<"请输入起始点的小区号:
"<cin>>start_name;
Min_Tree(G,V,start_name);
cout<<"铺设管道的最佳路径为:
"<Print_MinTree(V->firstedge);
cout<<"最佳路径的总费用为:
"<return0;
}
voidCreate_Algraph(Algraph*G)//创建无向网图
{inti;
EdgeNode*s,*t;
G->n=M;
G->e=E;
for(i=0;in;i++)
{
G->AdjList[i].vname=(char)(i+65);
G->AdjList[i].firstedge=NULL;
}
for(i=0;ie;i++)
{
s=(EdgeNode*)malloc(sizeof(EdgeNode));
t=(EdgeNode*)malloc(sizeof(EdgeNode));
s->name=a[i];
s->fee=fee[i];
s->flag=0;
s->next=G->AdjList[(int)b[i]-65].firstedge;
G->AdjList[(int)b[i]-65].firstedge=s;
t->name=b[i];
t->fee=fee[i];
t->flag=0;
t->next=G->AdjList[(int)a[i]-65].firstedge;
G->AdjList[(int)a[i]-65].firstedge=t;
}
//下面是手动输入网络信息的代码
/*inti;
chara,b;
floatf;
EdgeNode*s,*t;
for(i=0;i<9;i++)
{
G->AdjList[i].vname=(char)(i+65)