大型景区的管理系统C++实现

本次项目主要是设计一个大型景区的管理系统。系统用户包括管理员和游客两类,管理员负责管理景区的景点维护;游客可以根据自己的需求对景区进行各种信息查询,及路线规划等。
1.1系统背景
当今出行旅游已经是一件十分普遍的事情,而且随着经济的发展各个地方的旅游人数都在逐年上升。但是对于各个景区的负责人员来说旅游人数的递增给他们带来了许许多多的问题,例如景区没有足够的导游给游客们提供各景区介绍或者最快捷游览完所有景点的路径指导。而且游客乱停车的现象给景区的观光环境及秩序管理带来了很大的困扰。同样对于游客来说,他们却面临着无人提供指导服务以及停车难的问题。因此设计一个为游客自助提供服务并且便于景区管理人员对游客和车辆进行管理的软件系统是非常有必要且有发展前景的。
1.2系统功能需求
根据上述背景我打算设计的系统是一个大型的景区管理系统,系统的用户包括管理员和游客两类,管理员负责管理景区的景点维护而游客可以根据自己的需求对景区进行各种信息查询及路线规划等。
其中游客方面对系统的功能需求有:首先游客需要在任意时刻都能看到景区管理员发布的通知信息,其次游客能够按照景点关键字、景点热度、景点岔路数对景区的景点进行搜索与排序搜索得到的结果需要包含景区简介、有无休息区、有无厕所等信息;游客还能够查看景点的景区分布图,该分布图中显示景区每个景点到其他所有景点是否有直接通路、到达另一景点的路径长度以及大概需要花费的时间等信息;然后游客能够输入自己拟游览起点景区以及目标终点景区的名称从而获取系统提供的最短路径推荐;最后游客可以选定景区中任意一个景点,系统能帮游客找出以该景点为起点,能够保证游客可以将景区每个景点都去到并且最终返回该出发结点的最短路线推荐。
系统管理员方面对系统的功能需求有:为了防止游客错误地执行管理员才能够执行的操作,首先管理员要能够在进入系统之后进行管理员登录操作从而验证其管理员身份以便进行景区的管理;其次管理员主要的功能需求便是停车场系统这一部分,在该部分的功能当中管理员对到达停车场车辆的车牌信息以及到达时间进行记录并为车辆分配好一个停车的位置并将其位置记录;其次管理员需要对退出停车场的车辆进行结算收费并且为其他可能在等待停车的车辆分配车位;管理员能够随时查看当前时刻停车场车辆停靠信息以及便道上所停车辆的信息,确保已有车位的车辆在为了给其他需要离开停车场的车辆让道之后再次返回停车场内时能停靠至退让前的位置并且给按照时间顺序给在便道上等待的车辆依次安排停车位。
根据用户需求,我设计的本系统的用例图如下:
用例图
1.3系统解决方案
对于管理员部分的解决方案,设计一个Admin类当管理员登录之后实例化一个Admin类的对象,该管理员对象可以执行Admin类中的所有方法以实现管理员对景区的管理操作。该解决方案的设计原则是要实现应有的方法,确保管理员能够有效且无误地执行景点插入、景点删除、路径插入、路径删除、查看景点分布、发布通知公告、管理停车场等操作。该解决方案的设计优势是不仅能够保证管理员的所有操作都能顺利完成,还能运用登录的方法实现在同一平台上管理员与普通游客的区分,进而减少游客误操作的概率。
对于游客部分的解决方案,由于游客数目众多,因此不能在每个游客进入系统的时候都实例化一个游客的对象。我设计了一个所有游客都能够同用的TravelSystem类,该类中包含实现游客功能需求中例如查看景点分布图、景点的查找与排序、查找最短路径和最短距离、查看导游路线图等功能的所有方法。所有游客进入系统之后都能进行上述操作。
1.4主要工作
我开发本系统用到的实验环境是codeblocks编译器,操作系统是windows10。因此我的主要工作是按照以上设计系统时的需求分析按照面向对象程序设计的思想设计合适的类以及方法从而实现好所有用户所需要的功能。并且我还要在编程过程中运用分层设计等思想原则减少代码冗余,确保该软件系统的可靠性、安全性和高性能等特点和优势。
第二章 系统设计
2.1游客部分的系统设计
2.1.1景区分布图的数据结构
对于游客部分的功能设计我创建了一个TravelSystem类以用于对所有游客提供必要的服务,该类中有一个私有成员变量filename,由于景区的初始结构保存于文本文件中,当系统打开时,文本文件名传参至filename然后系统从文件中读取景区信息,初始化景区图结构。另外该类中还有另外一个私有的ALGraph类型的成员变量graph用于以邻接链表形式存储景区分布图的信息,带权无向图的邻接链表。ALGraph类是自定义的结构体类型,其中包含成员变量AdjList类型的vertices数组,数组中每个元素存储对应结点的基本信息,包括景点名称、景点简介、景点欢迎度、有无休息区、有无公厕以及以该结点为出发结点的边的链表ArcNode的头结点。而用于存储边的链表的结构体类ArcNode包含边所属结点、该边的长度、该边对应景区路径耗时程度以及边所指向的目的结点名称等信息。用于存储景区各景点的图的具体代码如下:
typedef struct ArcNode {//ArcNode变量类型存储边的信息
int adj;//所属哪个结点
float time;
int distance;
string destination;
ArcNode* nextArc;//指向下一顶点
} ArcNode;
typedef struct VNode {//存储景点的基本信息
string name;//景点名称
string intro;//景点简介
int popular;//景点欢迎度
bool rest_place;//有无休息区
bool washroom;//有无公厕
bool visible = false;
int edgeNum = 0;
ArcNode* headArc;
} VNode, AdjList[MAX_VERTEX_NUM];//邻接表(存储结点基本信息的数组,上限20个)
} ALGraph;

2.1.2文件信息数据结构
info.txt——存储景区信息
结构为:名称 简介 热度 是否有休息区 是否有厕所
示例:狮子山 形如狮子的山 9 0 1
泰山 东岳 10 1 1
spots.txt——存储景区路径
结构为:起点 终点 距离
示例:狮子山 泰山 2
狮子山 一线天 7

2.1.3输出邻接矩阵数据结构
输出景点的邻接矩阵,用二维数组实现该功能,输出矩阵中每个元素代表两景点之间的路径长度,同一元素作为起点和终点时路径长度为0因此输出矩阵的对角线上元素全部为0,其中不可达的景点之间距离用32767表示。
void TravelSystem::OutputGraph() {//输出图的信息,输出为邻接矩阵
int total = this->graph.vetexNum;
int spots = new int[total];
int matrix = new int[totaltotal];
int current = 0;
for (int m = 0; m < total; m++) {
for (int n = 0; n < total; n++) {
matrix[m
total+n]=(m==n ? 0 : has_Path(graph.vertices[spots[m]].name , graph.vertices[spots[n]].name));
}//对角线上元素取0,
}
for (int m = 0; m <total; m++) {
cout << setw(6) << setiosflags(ios::left) << setfill(’ ‘)
<< graph.vertices[spots[m]].name << " ";
for (int n = 0; n< total; n++) {
cout << setw(5) << setiosflags(ios::left) << setfill(’ ')
<< matrix[m*total+n] << " ";
}
cout << endl;
}
}

2.1.4景点查找排序设计
当游客需要依据景点关键字查询某一景点时可以采用C++当中的string中find函数以及string::npos参数在景点名称以及景点简介范围当中进行关键字匹配,find函数的返回值是整数,假如字符串存在包含关系,其返回值必定不等于npos,但如果字符串不存在包含关系,那么返回值就一定是npos。具体的实现代码如下:
void TravelSystem::search() {
string key;
int result = 0;
ALGraph& G = graph;
cout << endl << “请输入搜索关键词” << endl;
cin >> key;
size_t found1;
size_t found2;
cout << endl;
for (int i = 0; i < MAX_VERTEX_NUM; i++) {
if (G.vertices[i].visible == true) {
found1 = G.vertices[i].name.find(key);
found2 = G.vertices[i].intro.find(key);
if (found1 != string::npos || found2 != string::npos)
……
}

2.1.5最短路径与最短距离设计
求两个景点间的最短路径和最短距离,由MiniDistanse(G1,path,D)函数实现。在参观途中,如果游客需要查找从当前结点到某一结点的最短路径或最短时间时调用该方法。在本线路图中将输出任意景点间的最短路径和最短距离。算法采用迪杰斯特拉算法。具体实现办法为:定义两个二维数组,一个用于存储各结点间的最短距离,另一个用于存储最短路径下,每个结点的前一节点下标。在运用迪杰斯特拉算法计算完成后,可以对最短路径进行递推输出,并可以输出最短距离。代码设计如下:
void TravelSystem::ShortestPath(int* D, int* path, int* spots) {
int total = this->graph.vetexNum;
int u, v, w;
for (v = 0; v < total; v++) {
for (w = 0; w < total; w++) {
D[vtotal + w] = (has_Path(graph.vertices[spots[v]].name, graph.vertices[spots[w]].name));
if (D[v
total + w] < INFINITY) {
path[vtotal + w] = v;//对最短路径初始化为自身的前一个结点的序号
}
}
}
for (u = 0; u < total; u++) {
for (v = 0; v < total; v++) {
for (w = 0; w < total; w++) {
if (D[v
total + u] + D[utotal + w] < D[vtotal + w]) {
D[vtotal + w] = D[vtotal + u] + D[utotal + w];
path[v
total + w] = u;
}
}
}
}
}

2.1.6查看导游路线图设计
对游客来说,周游景点的方式有两种,第一种从入口A进入,最后从入口A出;另外一种从A进入,从另外的口B出。也就是遍历所有结点一遍的回路,而往往游客希望用最小耗费来遍历所有的景点,则该问题是一个最短哈密尔顿回路问题。运用提供的近似解法解决该问题对于无向图g:
void Traceablepath (Graph g)
{
(1)选择g的任一顶点r;
(2)用最小生成树算法找出带权图g的一棵以r为根的最小生成树T;
(3)前序遍历树T得到的顶点表L;
(4)将r加到表L的末尾,按表L中顶点次序组成回路H,作为计 算结果返回;
}
对于在此过程当中运用到的最小生成树问题可以使用辅助数组用于存储各个结点到当前生成的最小生成树的最近距离以及最近结点,初始值取决于选取的第一个结点,此后不断迭代,每次选取离当前生成树最近的点,直到所有点都被选中。具体数据结构代码如下:
typedef struct {
int adjvex;//某顶点与当前生成树的顶点之间权值最小的顶点
int lowdist;//某顶点与当前生成树的顶点之间的最小权值
} MiniEdge;//Prime算法辅助结构体

2.2管理员部分的系统设计
2.2.1景点插入与删除设计
管理员登录成功之后会实例化一个Admin类型的ad管理员对象,可以调用其中的insert_spot()方法以及delete_spot()方法进行景点的插入与删除操作,其具体实现原理就是调用insert_spot()方法,往图中存储结点信息的数组graph.vertices[ ]中新添加一个元素,并完善该元素的热度、简介、是否有厕所及休息区等成员变量。而删除结点即将数组中某个结点元素包含的成员变量的可见度信息设为false,即运用G.vertices[post].visible = false赋值方法而不是直接释放该数组对应位置上的内存,并且执行操作G.IsFull[post] = 0,把IsFull[]中对应位置赋值为0,等待下次需要添加结点的时候可以重新利用该数组对应位置上的空间并将已删除的结点的信息覆盖。最后需要注意的是删除该结点之后还需要遍历数组中的所有结点并判断是否与已删除结点存在可达边,如果有的话也需要从边的链表当中删除相应边的信息。具体设计代码如下:
void Admin::delete_spot() {
……//省略部分代码,仅保留关键代码
ALGraph& G = ts->graph;
G.vertices[post].visible = false;//不是直接删除而是将可见性设为0
G.IsFull[post] = 0;//定义该位置没满,下次输入的时候可以把该结点的信息覆盖
for (int i = 0; i<MAX_VERTEX_NUM; i++) {
if (G.vertices[i].visible == true) {
ts->delete_path(i, spot);//删除与该结点相关的边
}
}
ArcNode* prev = G.vertices[post].headArc;
ArcNode* temp;
while (prev != NULL && prev->nextArc != NULL) {
temp = prev->nextArc;
prev->nextArc = temp->nextArc;
delete temp;
}
delete prev;
G.vetexNum–;
……
}

2.2.2路径的插入与删除
路径的插入与删除分别调用Admin类中的insert_path()方法以及delete_path()方法,具体的实现原理是:对于insert_path()方法,要求管理员输入要删除的路径对应的起点、终点、距离等信息分别找到起点与终点在结点数组当中的位置并创建新的边结点加入对应结点的边链表。对于delete_path()路径方法,同样找到起点与终点结点的信息并删除链表中对应的边。具体的实现代码如下;

2.2.3通知公告功能设计
根据管理员输入的字符串型的通知内容将其存储到TravelSystem里面的一个私有成员变量billborad当中,并在每次用户选择各种操作时在控制台输出该公告信息语句。

2.3停车场系统设计
2.3.1停车场停车信息显示设计
以栈模拟停车场,以队列模拟车场外的便道,按照从终端读入的输入数据序列进行模拟管理。每一组输入数据包括三个数据项:汽车“到达”或“离去”信息、汽车牌照号码以及到达或离去的时刻。对每一组输入数据进行操作后的输出信息为:若是车辆到达,则输出汽车在停车场内或便道上的停车位置;若是车辆离去,则输出汽车在停车场内停留的时间和应交纳的费用其中在便道上停留的时间不收费。用一个栈来模拟停车场,先排队的车辆先离开便道进入停车场,符合队列的“先进先出,后进后出”的操作特点,因此,可以用一个队列来模拟便道。还需要有一个地方保存为了让路离开停车场的车辆,由于先退出停车场的后进入停车场,所以很显然保存让路车辆的场地也应该用一个栈来模拟。

2.4系统的关键类图设计如下:
在这里插入图片描述
第三章 系统实现
3.1 景区路线图的初始化实现
景区初始结构存储:景区各景点的详细信息存储在文本文件当中,其中spots.txt文件存储两个可达景点的名称以及他们之间的距离信息,而info.txt文件存储景点名称、景点简介、景点热度、是否有休息区以及是否有厕所等信息。当用户运行程序时进行景点信息的初始化,调用读取文件的函数,并将文件信息对应地存储到相应类中的相应成员变量当中。然后首先建立无向带权图,将顶点信息和边信息,存储在构建邻接链表中并且对景点信息简介、热度等信息进行补充。
管理员和游客在任意时刻都能以邻接链表和邻接矩阵两种形式对景区分布情况进行输出。当以邻接链表的形式输出景点信息时首先对邻接链表进行遍历输出,以便查看景点之间是否可达,即对应的图中两个景点之间是否存在边;而当以邻接矩阵的形式输出景点信息的时候需要进行邻接链表的转换,直观地输出景点之间的距离信息。

3.2管理员管理景区实现
因为本系统使用的用户有管理员以及普通游客,因此需要对他们进行区分。因此设计一个另外的管理员专用的菜单,菜单当中包含景点添加、景点删除、路径添加、路径删除、公告发布等功能。
对于景点添加,具体实现原理就是调用insert_spot()方法,该方法开始时判断当前景点数是否超过最大值如果当前结点数目没有到达最大值那么结点数组不会产生溢出错误,此时便可以调用FirstEmpty()函数通过遍历数组找到可以用于存储结点的空位,向该空位当中添加景区的具体信息。
至于景点删除功能的实现,由于用户输入的数据可能是各种各样的,例如某些管理员用户可能直接输入一个不存在的结点或者已删除的结点,为了保证程序的可靠性应该在最开始的时候就判断要删除的结点是否存在,不存在的话应该输出不存在的提示;如果要删除的结点存在就调用Locate(string u)函数找到该结点在数组中的位置,将它的visible成员变量设为false并将IsFull数组中该结点对应位置的值进行更改,再对图中其他结点依次遍历,对每个景点调用delete_path(int i, string dest)函数删除之前被删结点与该结点相连接的边。
对于路径插入功能的实现我最开始仍然是检查起点和终点是否存在,在确保两个结点都存在的前提下再对两结点之间的边的信息进行遍历与查询,如果存在边的邻接链表则调用insert_path(int i, ArcNode* node)方法对两个结点的邻接链表执行插入操作。最后路径删除功能的实现:调用delete_path(int i, string dest)函数对两个结点的邻接链表执行删除结点操作。

3.3停车场系统的实现
停车场系统中用到的数据结构有两个栈,其中一个用于维护停车场状态,另一个用于在汽车离开时即程序执行退栈操作的时候存储在该汽车之后进入停车场的汽车(结点),实现“后进先出,先进后出”过程。而在编写便道排队时,则需满足“先进先出,后进后出”,此时运用队列。
如果有车辆到达停车场的话首先应对其进行车牌信息、到达时间等的录入,通过判断栈空间是否存储满判断停车场是否还有空闲车位,如果栈空间没有满那么允许车辆进入停车场对应地执行压栈操作;如果栈空间已满,那么将该车辆加入等候区队列等候。
如果有车辆需要离开,那么首先需要检查该车在栈中的位置是否处于栈顶,如果并不是栈顶元素,那么就要把在该车辆之后进入停车场的车辆即在该车辆入栈之后再压栈的车辆退出停车场为它让路,然后该车才能顺利地退出停车场,为该车让路的所有车辆需要再按其原来进入停车场的次序进入停车场,这里运用到了栈的先进后出及后进先出的特性。最后检查在便道上是否有车等候,如果有车等候即等候队列不为空的时候就进行队列的先进先出操作。

第四章 系统测试
4.1管理员功能测试
进入系统主界面,选择管理员登录功能,登录成功之后开始测试
4.1.1景点插入功能测试
测试数据:插入景点名为东北大学,景点简介为:985工程大学,景点热度9星级,有休息区,有公厕。

测试结果:
在这里插入图片描述
插入之前:
在这里插入图片描述
插入之后:
在这里插入图片描述
可见插入操作成功执行。
4.1.2景点删除功能测试
测试数据:删除景点碧水潭
测试结果:
删除景点之前:
在这里插入图片描述
删除景点之后:
在这里插入图片描述
从以上输出结果易知删除结点的同时也删除了该结点与其他结点之间存在的边,因此删除操作成功执行。

4.2游客功能测试
4.2.1输出景点邻接矩阵
在这里插入图片描述
4.2.2最短路径查询
测试数据:
寻路起始景点:东北大学,寻路终止结点:北门
测试结果:
在这里插入图片描述
依据景点分布的邻接矩阵可以判断输出的路径结果即是从东北大学到北门的最短路径,且最短路径长度137也准确无误。

4.3停车场系统功能测试
4.3.1车辆到达登记与车位分配
测试数据:到达车辆牌照为123,到达时间为12:30
测试结果:
在这里插入图片描述
4.3.2车辆离开与价格结算:
测试数据:汽车位置为一号车位,离开时间为13:30
测试结果:
在这里插入图片描述
在这里插入图片描述
结果分析:由于系统预设定的停车费为42元/小时,因此价格输出正确。而且删除结点之后,停车场信息显示为空,因此该功能的实现准确无误。

第五章 结论
5.1实验心得与体会
通过这次这数据结构与算法课程设计的训练,我对上学期所学过的数据结构课程的知识有了更深的掌握和理解,这是一门纯属于设计的科目,它需用把理论变为上机实践。刚开始进行设计与编程的时候确实有很多地方我很不理解,但在老师的指导之下问题都得到了解决,老师给了我设计的整体思路,顺着老师的思路我一步步地实现了整个系统的功能。在实验过程当中同时也遇到了许多逻辑或者语义上的错误,有时候看不出到底是哪里出现了错误,但是程序还是得继续下去,我多次请教了老师和同学,逐渐能自己找出错误,并加以改正。在这一段努力学习的过程中,我的编程设计有了明显的提高。
这次开发的系统具有很实用的意义,加之我们刚好学习了软件工程这门课程,依据软件工程课当中所学到的内容我在设计本系统的时候进行了系统背景分析,系统用户以及用例分析然后还充分运用了面向对象程序设计的思想编写代码。同时我对图Graph、链表Linkdlist、队列Queue还有栈Stack有了进一步的理解与认识。同时对图的遍历算法有了熟练的掌握,运用了最小生成树算法,迪杰斯特拉算法,运用先序遍历构建汉密尔顿回路。同时正确地运用了队列的先进先出特征以及栈的先进后出特征解决了实际问题。总之还需进一步通过实践来提高对各种数据结构当中所蕴含的思想进行更深的掌握,同时学习好这些数据结构对应的算法并能够在实际生活当中运用这些数据结构进行编程开发出更加实用的软件系统。
5.2系统实现的功能
本次所编写的景区管理系统主要用户有两类:普通游客与景区管理员。游客部分实现的基本功能包括对景点的查找以及排序、查找景点之间的最短路径以及查询最优导游路线图;管理员则能对存储景区的图进行结点以及边的增删改查,并安排车辆停车位及计算费用。
5.3系统创新点
在管理员部分的操作当中,当选择根据输入景点删除图中对应结点的时候由于结点信息都存在数组当中,数组中每个元素都是结点类型的结构体。在结点的结构体当中设置一个布尔类型的变量决定它是否可见还是不可见,删除某结点时可以直接将其设置为不可见而不是删除整个数组然后再定义一个存储删除该结点后新的数组。而且删除该结点之后并不是将该块内存空间浪费,当下次再加入新结点的时候可以把以前被删除结点的信息用新结点的对应信息覆盖。这部分运用的是另外一个判断数组各位置是否已满的另外一个数组来实现,当删除某结点后,该结点对应位置设置信息为未满。
5.4系统待解决问题
在游客选择导游路线图的时候,采用的算法是实验指导书当中给出的一个参考算法,但我认为该算法在寻路的时候并不是最优,因此可以运用更好的算法解决最优导游路径寻路的问题。

展开阅读全文

没有更多推荐了,返回首页