长春大学课程设计内页.docx
《长春大学课程设计内页.docx》由会员分享,可在线阅读,更多相关《长春大学课程设计内页.docx(32页珍藏版)》请在冰豆网上搜索。
长春大学课程设计内页
一、设计题目
全国交通咨询模拟软件
二、设计目的
出于不同目的的旅客对交通工具有不同的要求。
例如,因公出差的旅客希望在旅途中的时间尽可能的短,出门旅游的游客则期望旅费尽可能省,而老年旅客则要求中转次数最少。
编制一个全国城市间的交通咨询程序,为旅客提供两种或三种最优决策的交通咨询。
三、设计分析
提供对城市信息进行编辑(如:
添加或删除)的功能。
城市之间有两种交通工具:
火车和飞机。
提供对列车时刻表和飞机航班进行编辑(增设或删除)的功能。
提供两种最优决策:
最快到达和最省钱到达。
全程只考虑一种交通工具。
旅途中耗费的总时间应该包括中转站的等候时间。
咨询以用户和计算机的对话方式进行。
由拥护输入起始站、终点站、最优决策原则和交通工具,输出信息:
最快需要多长时间才能到达或者最少需要多少旅费才能到达,并详细说明依次于何时乘坐哪一趟列车或哪一次班机到何地。
对全国城市交通图和列车时刻表及飞机航班表进行编辑,应该提供文件形式输入和键盘输入两种方式。
飞机航班表的信息应包括:
起始站的出发时间、终点站的到达时间和票价;列车时刻表则需根据交通图给出各个路段的详细信息,例如:
基于教科书7.6节图7.33的交通图,对从北京到上海的火车,需给出北京至天津、天津至徐州及徐州至上海各段的出发时间、到达时间及票价等信息。
以邻接表作交通图的存储结构,表示边的结构内除含有邻接点的信息外,还应包括交通工具、路程中耗费的时间和花费以及出发和到达的时间等多种属性。
另增加旅途中转次数最少的最优决策。
这个咨询系统可以回答旅客提出的各种问题。
例如:
一位旅客要从A城市到城市,他希望选择一条途中中转次数最少的路线。
假如图中每一站都需要换车,则这个问题反映到图上就是要找一条从顶点A到B所含边的数目最少的路径。
我们只需从顶点A出发对图作广度优先搜索,一旦遇到顶点B就终止。
由此所得广度优先生成树上,从根顶点A到顶点B的路径就是中转次数最少的路径,路径上A与B之间的顶点就是途径的中转站数,但是,这只是一类最简单的图的最短路径问题。
有时,对于旅客来说,可能更关心的是节省交通费用;而对于司机来说,里程和速度则是他们感兴趣的信息。
为了在图上表示有关信息,可对边赋以权,权的值表示两城市见的距离,或途中所需时间,或交通费用等等,此时路径长度的度量就不再是路径上边的数目,而是路径上边的权值之和,这就要用到迪杰斯特拉算法和弗洛伊德算法解决这些问题。
由此设计、实现一个全国大城市间的交通咨询程序,为旅客提供三种最优决策方案:
时间最短、费用最小、中转次数最少。
在程序中输入城市名称时,需输入10个字母以内的字母串;输入列车或飞机编号时需输入一个整型数据;输入列车或飞机的费用时需输入一个实型数据;输入列车或飞机开始时间和到达时间时均需输入两个整型数据;在选择功能时,应输入与所选功能对应的一个整型数据。
程序的输出信息主要是:
最快需要多少时间才能到达,或最少需要多少旅费才能到达,或最少需要多少次中转到达,并详细说明依次于何时乘坐哪一趟列车或哪一次班机到何地。
程序的功能包括:
提供对城市信息的编辑,提供列车时刻表和飞机航班表的编辑,提供三种最优决策:
最快到达、最省钱到达、最少中转次数到达。
功能结构图如图1所示
图1系统功能结构图
四、总体设计
1.系统用到的抽象数据类型定义:
ADTGraph{
数据对象V:
一个集合,该集合中的所有元素具有相同的特性。
数据关系R:
R={VR}
VR={|P(x,y)^(x,y属于V)}
基本操作:
(1)initgraph(&G);
(2)CreateGraph(&G);
(3)EnterVertex(&G);
(4)DeleteVertex(&G);
(5)EnterplaneArc(&G);
(6)DeleteplanArc(&G);
(7)EntertrainArc(&G);
(8)DeletetrainArc(&G);
}ADTGraph
ADTLinkQueue{
(1)InitQueue(&Q);
(2)IsEmpty(&Q);
(3)EnterQueue(&Q,x);
(4)DeleteQueue(&Q,&y);
}ADTLinkQueue
ADTTimeTree{
数据对象D:
一个集合,该集合中的所有元素具有相同的特性。
数据关系R:
若D为空,则为空树。
若D中仅含有一个数据元素,则R为空集,否则R={H},H为如下二元关系:
(1)在D中存在唯一的称为根的数据元素root,它在关系H中没有前驱
(2)除root以外,D中每个结点在关系H下有且仅有一个前驱。
(1)CreateTimeTree(p,i,j,&Q,infolistarcs);
(2)CopyTimeTree(p,q);
(3)VisitTimeTree(p);
}ADTTimeTree
2.各程序模块之间的调用关系
主函数可调用子程序如图所示
图1调用子程序图
图2调用子程序
图3调用子程序
图4调用子程序
五、详细设计
1.创建交通图算法的伪码描述如下:
intLocateVertex(ALGaph*G,char*v)/*找出城市名在图中对应结点位*/
{
for(k=0;k<图G中的结点个数G->vexnum;k++)
if(第k个结点中的城市名与传过来的城市名相同)
{
j=k;/*记录位置*/
break;
}
}
返回k的数值;
}
intCreatGraph(ALGraph*G)
{
if(打开城市文件,文件指针返回值为空)
{
输出错误文件信息;程序返回值为0;
}
while(文件不为空)
{
将文件指针所指的字符串读到城市名数组city[i]中;
i++;
}
关闭文件;
j=0;
while(j<城市个数)
{
将city[i]中的内容复制到图的结构体的结点数组中;
图的结构体其他项负初值;
j++;
}
G->vexnum=i;
打开航班信息文件"plane.txt";
将文件中的内容以块为单位读到缓冲区数组a中;
关闭文件;]
a的计数变量k=0;
弧的计数变量arc_num=0;
while(k<信息个数)
{
调用函数LocateVertex(G,a[k].vt)得到起始结点的位置i;
调用函数LocateVertex(G,a[k].vt)得到起始结点的位置j;
q=G->vertices[i].planfirstarc;
m=0;
while(q!
=NULL)
{
if(弧q中的邻接顶点与j相等)
{
将数组a[i]中的内容都复制到弧q中;
m=1;
break;
}
q=q->nextarc;
if(m=0);
{
开辟一个弧结点;
将数组a[i]中的内容都复制到新的弧结点中;
将弧结点连接到适当的位置中去;
arc_num++;
}
k++;
}
G->planarcnum=arc_num;
打开列车信息文件"plane.txt";
将文件中的内容以块为单位读到缓冲区数组a中;
关闭文件;]
a的计数变量k=0;
弧的计数变量arc_num=0;
while(k<信息个数)
{
调用函数LocateVertex(G,a[k].vt)得到起始结点的位置i;
调用函数LocateVertex(G,a[k].vt)得到起始结点的位置j;
q=G->vertices[i].trainfirstarc;
m=0;
while(q!
=NULL)
{
if(弧q中的邻接顶点与j相等)
{
将数组a[i]中的内容都复制到弧q中;
m=1;
break;
}
q=q->nextarc;
if(m=0);
{
开辟一个弧结点;
将数组a[i]中的内容都复制到新的弧结点中;
将弧结点连接到适当的位置中去;
arc_num++;
}
k++;
}
G->trainarcnum=arc_num;
返回;}
2.创建航班算法的伪码描述如下:
creatplanefile()
{while(flag)/*flag为标志位,初值为1*/
{提示“输入航班信息”;
输入航班code;
输入航班的出发城市vt;
输入航班的到达城市vh;
输入机票价格money;
输入航班的出发时间bt;
输入航班的到达时间at;
a.[count].co=code;/*a为程序头部定义的结构体*/
strcpy(a.[count].vt,vt);
strcpy(a.[count].vh,vh);
a.[count].bt=bt;
a.[count].at=at;
a.[count].mo=money;
计数值count+1;
提示“是否要继续输入航班信息:
”;
scanf(“%d”,&flag);
}
if(航班文件不能以读写形式打开)
提示“无法打开文件”;
将计数值count写入航班车文件;
for(i=0;iif(无法将a[i]写入航班文件)
提示“文件无法写入”;
关闭航班文件;
}
3.删除城市结点算法的伪码描述如下:
DeleteVertex(ALGraph*G)/*G是程序头部定义的结构体*/
{
提示“输入删除城市名”;
gets(城市名:
v);
提示“是否确定要删除(Y/N)“;
c=getchar();
if(c==’Y’||c==’y’)
{n=0;/*0是记数标志,控制循环次数*/
while(n<图G表头接点总个数&&图G的存储城市名与v不同)
/*G表头结点总个数比实际大1*/
记数值n+1;
if(n==图G表头结点总个数)
提示“无法找到此城市“;
else
{
i=LocateVertex(G,v);
/*利用G函数找到此城市名所处在G中位置*/
删除从此结点出发的所有航班弧;
删除从此结点出发的所有列车弧;
for(j=i;j<图G表头结点总个数-1;j++)
将G第j个结点的信息依前移1位;
将G第j个结点的信息制空;
/*以下是删除所有指向此结点的航班弧*/
for(k=0;k<图G表头记点总个数-1;k++)
{p指向图G中k结点的第一条飞机弧;
while(p!
=NULL)
{if(该弧指向的顶点位置(p->adjvex)>i)
{将该弧指向顶点位置-1;
q=p;
p指向下一条飞机弧;
elseif(该弧指向的顶点位置(p->adjvex)==i)
{if(p指向图G中k结点的第一条飞机弧)
{m=p;
将图G中k结点的第二条飞机弧改为第一弧;
p指向下一条飞机弧;
释放(m);
}
else
{将p的下一条弧赋给q的下一条弧;
m=p;
p指向下一条飞机弧;
释放(q);
}
}
else
{q=p;
p指向下一条飞机弧;
}
}
}
/*以下是删除所有指向此结点的列车弧*/
for(k=0;k<图G表头记点总个数-1;k++)
{p指向图G中k结点的第一条列车弧;
while(p!
=NULL)
{if(该弧指向的顶点位置(p->adjvex)>i)
{将该弧指向顶点位置-1;
q=p;
p指向下一条列车弧;
}
elseif(该弧指向的顶点位置(p->adjvex)==i)
{if(p指向图G中k结点的第一条列车)
{m=p;
将图G中k结点的第二条列车弧改为第一弧;
p指向下一条列车弧;
释放(m);
}
else
{将p的下一条弧赋给q的下一条;
m=p;
p指向下一条列车弧;
释放(q);
}
}
else
{q=p;
p指向下一条列车弧;
}
}
}
}
图G表头结点总个数-1;
}
elsereturn;
}
4.求城市v0,v1之间的最少费用算法的伪码描述如下:
ExpenditureDispose()
{for(v=0;v<城市个数;v++)
{
城市v还未求得最少费用;
*(D+v)=城市v0到v的最少费用;
将城市v的路径设置为空;
if(*(D+v)将城市v0和v加入到城市v的路径中;
}
城市v0到城市v0的最少费用为0;
城市v0设为已求得最少费用;
for(i=1;i<城市个数;v++)
{m=INFINITY;
for(w=0;w<城市个数;w++)
if(城市w未求得最少费用)
if(*(D+w){v=w;
m=*(D+w);
}
if(v等于v1)
{
根据城市v的路径输出从城市v0到城市v1所需经过的城市及路线;
输出最少费用*(D+v1);
返回;
}
else
{
将城市v设为已求得最少费用;
for(w=0;if(城市w未求得最少费用并且从城市v到w有路径)
{求出从城市v到城市w的最少费用及路线;
if(*(D+w)>m+城市v到w的最少费用)
{*(D+w)=m+城市v到w的最少费用;
将城市w的路径改成城市v的路径并在最后加入城市w;
}
}
}
输出没有列车或飞机从城市v0到v1;
}
六、系统调试
1.原设计在边的属性中加上访问标志域mark,意图以!
(p->mark)来判别是否要检测该条边,后发现,如此只能求出第一对顶点之间的最短路径、关键路径(时间最短、费用最小、中转次数最少)。
在继续求其他的最短路径、关键路径(时间最短、费用最小、中转次数最少)时,必须恢复所有边的访问标志为FALSE,则需要耗费O(e)的时间,并不比现在的算法优越,故舍去之。
2.考虑到对交通图的初始化和列车时刻表及飞机航班表的数据实在多,故提供文件形式输入和键盘输入两种方式。
故在程序中定义了createcityfile():
创建城市文件的子程序,createplanefile():
创建航班文件的子程序,createtrainfile():
创建列车时刻表文件的子程序,所以只要对相应数据在文件中正确编辑就可以实现对系统的各项操作,如果要增加和删除城市、航班、列车,也可以从键盘上操作来实现。
3.以邻接表作交通图的存储结构,表示边的结点内除含有邻接点的信息外,还包括交通工具、路程中消耗的时间和花费以及出发和到达的时间等多项属性。
其空间复杂度为O(e),此时的时间复杂度也为O(e)。
构造邻接表的时间复杂度为O(n+e),输出路径的时间复杂度为O(e)。
由此,本交通咨询系统程序的时间复杂度为O(n+e)。
4.由于交通咨询系统程序在实际执行时,需要根据用户的临时输入求最短路径、关键路径(时间最短、费用最小、中转次数最少)。
因此,虽然迪斯特拉算法的时间复杂度比弗洛伊德算法低,但每求一条最短路径、关键路径都必须重新搜索一遍,在频繁查询时导致查询效率降低,而弗洛伊德算法只要计算一次,即可求得每一对顶点之间的最短路径、关键路径,虽然时间复杂度为O(n3),但以后每次查询都只要查表即可,极大地提高了查询效率,而且,弗洛伊德算法还支持带负权的图的最短路径的计算。
由此可见,在选用算法时,不能单纯地只考虑算法的渐进时间复杂度,有时还必须综合考虑各种因素。
5.本程序的运行环境为DOS操作系统,执行文件为:
jiaotong.exe。
6.运行程序,首先出现主界面。
主界面包括四个选项:
选项一:
管理员管理界面选择该项可进行城市交通系统的管理,具体使用说明见说明2;选项二:
用户咨询界面,选择该项可进行最少费用、最少时间、最少中转次数的决策咨询,具体使用见说明7;选项三:
显示城市交通系统程序,选择该项可显示城市交通系统的所有信息,包括城市、航班和列车车次;选项四:
退出程序,选择该项将退出程序。
7.管理员管理界面包括5个选项:
选项一:
初始化城市交通系统界面,进行城市交通系统的初始化,具体使用见说明3;选项二:
城市编辑界面,可进行城市的增加和删除,具体使用见说明4;选项三:
航班编辑界面,可进行航班的增加和删除,具体使用见说明5;选项四:
列车车次编辑界面,可进行列车车次的增加和删除,具体使用见说明6;选项五:
返回上一级菜单,可返回主界面。
8.列车车次编辑界面包括两个选项:
选项一:
增加列车车次,可在两个城市之间新增列车车次,选择该项后用户需输入新增列车的编号,起始城市,到达城市及费用、时间等信息;选项二,删除列车车次,可删除两个城市间的一条列车车次,选择该项后用户需输入要删除车次的编号,起始城市,到达城市的信息,若列车车次不存在或编号、城市输入有误,程序将提示错误。
9.用户咨询界面包括四个选项:
选项一:
最少费用咨询;选项二:
最少时间咨询;选项三:
最少中转次数咨询;选项三:
返回上级菜单,可返回主界面。
选择选项一、二、三都要求用户输入咨询信息,包括起始城市,到达城市和交通工具。
输入完毕后城市提示用户是否确认,若不确认则要求用户重新输入咨询信息,若确认则给出用户所需的最优决策信息。
七、结论
本学年自从刚学习《数据结构》这门课程,就被其深深的吸引。
在老师的帮助下,我有幸一步步加深了对这门课程和C语言的认识。
通过这次课程设计,加强了我对学习C语言的热情。
只有想不到,没有做不到。
只要自己努力,我想结果会是美好的。
通过这个学期我对C学习,我感觉到了C编写程序的重要性。
学编程急不的,首先你要有一定的编程基础。
并留下一个对C编程难的不得了的印象。
其实,只要踏踏实实一步一步来,你会发觉原来编程难度也不过如此。
虽说学C并不是传说的那么难,可不下些苦工夫是学不成的.在学数据结构前,你必须确定,你是因为热爱编程才学数据结构的,而不是出于炫耀或其他.否则,恐怕你坚持不到胜利的那一天。
但在这段时间的课程设计过程中,我又学到了很多东西:
C++的学习要循序渐进,不能因为学过其它基础语言而放弃对它基础的学习,学习C++同样要打好基础;平时多编程序,语言的学习关键在于上机,只有在机器上多写程序,自己的水平才会不断发现和解决问题;要多思考,遇到问题或不解时不能不懂装懂,要尽量去想请清其缘由,要懂得查阅有用的资料。
课设给了我一次机会,也给了我很大的帮助。
八、附录
#include
#include
#defineMAX10//最大顶点数
#defineEDGESMAX999999//边最大权值
/**************结构体*****************/
structcity//城市结构体,包括城市名字以及城市在图中的编号
{
intNumber;
charName[10];
};
structstone//图的存储结构
{
intvexs[MAX],n,e;//顶点表,顶点数,边数
intdistancedge[MAX][MAX],timedge[MAX][MAX],costedge[MAX][MAX];//邻接矩阵,分别是距离,时间,费用
};
/************函数声明******************/
voidPRINT(structstone*g);
//全局变量,被CreatGraph(),PRINT()等函数调用..........................
structcityCit[MAX];
voidCopyRight()
{
printf("\t欢迎使用交通咨询系统\n");
system("PAUSE");
}
//根据城市名字取其序号,序号取其名字,对于一个大型的城市网络图来说,必须输入名称对应序号,而不可能让用户输入序号
intGetCityNum(charcityname[])//城市名字->序号
{
for(inti=0;i{
if(strcmp(Cit[i].Name,cityname)==0)
return(Cit[i].Number);
}
printf("您输入的城市名称\"%s\"有错误\n",cityname);
return-1;
}
char*GetCityName(inti)//城市序号->名字
{
if(i<0||i>MAX)
{
printf("参数越界,发生错误了!
");
exit(-1);//0正常退出,其他为非正常退出,此处无所谓
}
returnCit[i].Name;
}
/************[1]邻接矩阵构建有向图*************************/
voidCreatGraph(structstone*g)
{
FILE*fp;
fp=fopen("c:
\\stone.txt","r");
if(fp==NULL)
printf("无法打开交通网络图文件!
\n");
else
{
fscanf(fp,"%d%d",&(g->n),&(g->e));//顶点数,边数
inti=0,j=0;
for(i=0;i<(g->n);i++)//城市顶点,序号和名字
{
fscanf(fp,"%d",&(g->vexs[i]));//printf("%d",(g->vexs[i]));
Cit[i].Number=g->vexs[i];//printf("%d",(Cit[i].Number));
fscanf(fp,"%s",Cit[i].Name);//printf("%s",Cit[i].Name);
}
for(i=0;i<(g->n);i++)//初始化邻接矩阵
for(j=0;j<(g->n);j++)
g->distancedge[i][j]=g->timedge[i][j]=g->costedge[i][j]=0;
for(i=0;i<(g->n);i++)//有权图的邻接矩阵读取,是否为有向图,根据STONE.TXT文件内容而定
for(j=0;j<(g->n);j++)//EDGESMAX=999999,距离权值矩阵
fscanf(fp,"%d",&(g->distancedge[i][j]));
for(i=0;i<(g->n);i++)//time权值矩阵
for(j=0;j<(g->n);j++)
fscanf(fp,"%d",&(g->timedge[i][j]));
for(i=0;i<(g->n);i++)//cost权值矩阵
for(j=0;j<(g->n);j++)
fscanf(fp,"%d",&(g->costedge[i][j]));
}
fclose(fp);
}
/*******************[2]Dijkstra[一个顶点到其余顶点]**********************/
voidDijkstra(intn,intedges[][MAX],intv0,intdis