目录
一. 设计目的
二. 设计背景
三. 系统功能
四. 系统算法实现
五. 系统调试与结果分析
六. 完整源代码
一. 设计目的
通过设计并实现校园导游系统,使学生对数据结构有更深入的了解。该系统综合性非常广,能够极大提高学生的设计,编程及调试等解决实际应用的能力。培养对实际问题的分析,建模等逻辑思维。
二. 设计背景
随着校园规模的扩大,游客和学生对校园内景点信息的需求日益增加。传统的纸质地图已难以满足其需求,因此开发一款校园导游系统显得尤为重要。本系统以湖南城市学院为例,实现了基于Dijkstra算法的最短路径查询功能,以及景点信息查询和推荐等功能。
三. 系统功能
1)地图展示功能:首先系统能够展示校园景点的大致分布图,帮助用户直观了解校园布局。知道校园有哪些景点可以前往。
2)推荐景点模块:在查看了整个校园大致分布图后,如果用户不知道该前往何处,就可以查看推荐景点,前往合适自己前往的景点。
3)景点信息查询模块:用户若在地图上不熟悉景点的信息以及作用,就可以输入景点编号,查看景点的所有信息简介。
4)景点间最短路径的查询:当用户已经明确了自己想要前往的景点,就可以输入用户当前所在位置以及想要前往的景点,系统就会给出到达目标景点的最短路径,切实帮助用户快速到达目的地。
四. 系统算法实现
系统在查询景点间最短路径当中,就使用了Dijkstra算法。该算法主要用于计算图中一个顶点到其他所有顶点的最短路径。算法核心是通过不断选取未访问的顶点中距离起点最近的顶点,并更新其相邻顶点的距离,直到所有顶点都被访问过。
实现该算法主要使用如下四个函数:
Create函数用来初始化校园景点图,包括设置顶点数、边数、初始化邻接矩阵、输入景点信息及道路信息等。
inquire函数:根据用户输入的景点编号,输出该景点的详细信息。
Dijkstra函数:实现Dijkstra算法,计算并输出从起点到终点的最短路径及长度。
PrintPath函数:辅助函数,用于打印从起点到终点的最短路径。
五. 系统调试与结果分析
首先用户会看到这样的一个交互界面,这是文本形式的交互界面,所以这个可以作为我们这个系统的改进,未来可以考虑增加图形用户界面(GUI),提高用户体验。
接着,我们可以查看整个学校的平面图,输入操作1:
只要我们的用户没有执行退出操作,就可以一直使用校园导游系统。
假设我们用户此时在编号为1,也就是学校北门的位置,想要前往学校图书馆,也就是编号为5的景点,想要知道前往图书馆的最短路径,方便她快速到达,就可以执行第4个操作,如下所示:
系统就会给出最短路径长度,以及最短路径,也就是从学校北门先经过篮球场,最后到达图书馆,就是最短的路径。
用户如不需要再使用该系统就可以执行第5步操作,退出我们的校园导游系统,如下所示:
六. 完整源代码
//基于Dijkstra的校园导游系统。
//这里我们以湖南城市学院大学的部分景点为例
#include<stdio.h>
#include<string.h>
#include<limits.h>;
//首先确定一个景点所包含的内容:景点名称,景点简介
#define MAXSIZE 100 //定义一个数组最大值
typedef struct ElemType {
int id; //为了方便管理,设置一个顶点编号
char name[MAXSIZE]; //景点名称,用一个字符数组存储
char introduce[MAXSIZE]; //景点的简介,也用字符数组存储起来
}ElemType;
//构建一个图,使用邻接矩阵来存储
typedef struct Graph {
ElemType vexs[MAXSIZE]; //存放顶点
int arcs[MAXSIZE][MAXSIZE]; //邻接矩阵
int vexnum,arcnum ; //顶点数和边数
}Graph;
//函数声明
void map();
void choice();
void recommend();
Graph Creat(Graph& G);
void inquire(Graph G, int n);
void PrintPath(Graph G, int prev[], int start, int end);
void Dijkstra(Graph& G, int start,int end);
int main()
{
Graph G=Creat(G); //创建一个图,并对这个图进行相应的初始化和信息录入
while (1) {
int num;
int num1; //用来接收第二个操作的景点编号
printf("\n");
choice(); //菜单界面
scanf("%d", &num);
switch (num) { //使用一个switch语句来处理用户所要进行的操作
case 1:map(); break; //break使这个switch语句结束
case 2:printf("请输入要查询的景点编号(1~10):\n");
scanf("%d", &num1);
inquire(G, num1);
break;
case 3:recommend(); break;
case 4:printf("请输入您现在所在景点的编号,以及想要去的景点编号:\n");
int start, end;
scanf("%d %d", &start, &end);
Dijkstra(G,start,end);
break;
case 5:printf("感谢使用本校导游系统,欢迎下次再来");
return 0; //整个函数结束
default:printf("请输入正确的操作指令");
break;
}
}
}
//首先创立一个函数来展示学校平面图
void map() {
//打印出学校景点的大概方位
printf("\n");
printf("--------------------\n");
printf("·····1·····\n");
printf("2···· 3·····\n");
printf("··4·· 5···6 7\n");
printf("··8········\n");
printf("9···· 10·····\n");
printf("---------------------\n");
printf("地图备注:1--学校北门 2--老食堂 3--篮球场\n");
printf("4--操场 5--图书馆 6--教学楼1 7--教学楼2\n");
printf("8--新食堂 9--宿舍楼 10--学校南门\n");
}
//接着创建一个菜单供用户选择
void choice() {
printf("********欢迎来到湖南城市学院********\n");
printf("------------------------------------\n");
printf("****** 1.湖南城市学院整体平面图 ****\n");
printf("****** 2.查询所有景点信息 ****\n");
printf("****** 3.查看推荐前往景点 ****\n");
printf("****** 4.查询到达景点的最短路径 ****\n");
printf("****** 5.退出 ****\n");
printf("------------------------------------\n");
printf("请输入您想要进行的操作:\n");
}
//构造一个提供推荐景点的函数
void recommend() {
printf("老食堂推荐指数:*****\n");
printf("老食堂一楼二楼都有许多特色美食,有些菜品,口感鲜美,让人陶醉;有些佳肴,香气扑鼻,让人无法自拔。\n");
printf("在品完美食后,还能去一楼蜜雪冰城,益禾堂,瑞幸咖啡等知名品牌店去挑选一杯下午茶,可谓是快活似神仙啊\n");
printf("吃饱喝足才有力气,热爱美食的朋友们可千万不要错过哦\n");
printf("\n");
printf("学校图书馆推荐指数: ****\n");
printf("学校图书馆收藏着大量图书,各种各样的图书应有尽有。丰富的阅读让思想的翅膀翱翔,让你轻松漫步在知识的丛林,开启智慧的翅膀\n");
printf("向着本就属于你的高度往上攀登,终能站在巅峰");
printf("丰富的物质固然重要,但丰富的精神内涵也缺一不可\n");
}
//开始录入景点信息
Graph Creat(Graph& G) {
G.vexnum = 10; //已知景点总数为10
G.arcnum = 19;
//从1到10给景点编号
for (int i = 1; i <= G.vexnum; i++) {
G.vexs[i].id = i;
}
//开始初始化道路
for (int j = 1; j <= G.vexnum; j++) {
for (int k = 1; k <= G.vexnum; k++) {
G.arcs[j][k] = INT_MAX;
}
}
//开始根据实际情况输入有道路的两个顶点
G.arcs[1][2] = G.arcs[2][1] = 300; //道路是互通,能从一个景点到另外一个景点,也能从另外一个景点到这个景点
G.arcs[1][3] = G.arcs[3][1] = 100;
G.arcs[2][3] = G.arcs[3][2] = 150;
G.arcs[2][4] = G.arcs[4][2] = 160;
G.arcs[2][8] = G.arcs[8][2] = 310;
G.arcs[2][9] = G.arcs[9][2] = 350;
G.arcs[3][5] = G.arcs[5][3] = 110;
G.arcs[3][6] = G.arcs[6][3] = 250;
G.arcs[4][8] = G.arcs[8][4] = 170;
G.arcs[4][9] = G.arcs[9][4] = 260;
G.arcs[5][6] = G.arcs[6][5] = 180;
G.arcs[5][10] = G.arcs[10][5] = 270;
G.arcs[5][8] = G.arcs[8][5] = 280;
G.arcs[6][10] = G.arcs[10][6] = 320;
G.arcs[6][7] = G.arcs[7][6] = 120;
G.arcs[7][10] = G.arcs[10][7] = 330;
G.arcs[8][9] = G.arcs[9][8] = 190;
G.arcs[8][10] = G.arcs[10][8] = 200;
G.arcs[9][10] = G.arcs[10][9] = 210;
//开始输入景点的名称和简介
strcpy(G.vexs[1].name, "学校北门");
strcpy(G.vexs[1].introduce,"一个供学生自由出入的大门,北门外面有着学校周边数量最多的美食摊位。");
strcpy(G.vexs[2].name,"老食堂");
strcpy(G.vexs[2].introduce,"窗口众多,饭菜可口,是舌尖上的美味");
strcpy(G.vexs[3].name,"篮球场");
strcpy(G.vexs[3].introduce,"强身健体,增进友谊,挥发汗水的宝地");
strcpy(G.vexs[4].name,"操场");
strcpy(G.vexs[4].introduce,"每晚都不缺夜跑的人,有许多社团活动也在这个操场举行");
strcpy(G.vexs[5].name,"图书馆");
strcpy(G.vexs[5].introduce,"一座充满学习氛围的殿堂,里面的每一位学生都在诠释着努力上进的姿态");
strcpy(G.vexs[6].name,"教学楼1");
strcpy(G.vexs[6].introduce,"平日里学生们经常上课的地方,里面布满了桌椅");
strcpy(G.vexs[7].name,"教学楼2");
strcpy(G.vexs[7].introduce,"学生们上课的第二场所");
strcpy(G.vexs[8].name,"新食堂");
strcpy(G.vexs[8].introduce,"里面大多是新开的窗口,是吃腻了老食堂的第二选择");
strcpy(G.vexs[9].name,"宿舍楼");
strcpy(G.vexs[9].introduce,"我们大家都需要一个港湾,而宿舍就是我们在学校的港湾,用来放松休息");
strcpy(G.vexs[10].name,"学校南门");
strcpy(G.vexs[10].introduce,"学校与北门遥遥相对的一道门");
//在完成上述初始化,以及信息录入之后,返回这个图
return G;
}
//创建一个函数用来输出一个景点的信息
void inquire(Graph G, int n) {
if (n < 1 || n>10)
printf("输入的景点编号有误(请输入1~10的景点编号)\n");
else {
printf("编号为%d的景点信息如下所示:\n", G.vexs[n].id);
printf("该景点名称为%s\n", G.vexs[n].name);
printf("该景点的简介:%s\n", G.vexs[n].introduce);
}
}
//辅助函数,用来打印从start到end的路径
void PrintPath(Graph G,int prev[], int start, int end) {
if (end == start) {
printf("%s", G.vexs[end].name);
return;
}
PrintPath(G,prev, start, prev[end]); //递归调用这个算法,直到把所有路径都打印出来
printf("-> %s", G.vexs[end].name);
}
//创建一个函数来求得任意两个顶点间的最短路径
void Dijkstra(Graph& G, int start, int end) {
int dist[MAXSIZE]; //用来存放初始顶点到其他所有顶点的距离
int visited[MAXSIZE]; //用来存放这个顶点是否被访问过
int prev[MAXSIZE]; //用来存放前驱顶点
//初始化距离和前驱结点
for (int i = 1; i <= G.vexnum; i++) {
dist[i] = INT_MAX;
visited[i] = 0;
prev[i] = -1;
}
dist[start] = 0; //起始顶点到自身距离为0
//开始遍历所有顶点
for (int count = 0; count < G.vexnum - 1; count++) {
int u = -1;
//找出未访问的顶点中距离最小的顶点
for (int i = 1; i <= G.vexnum; i++) {
if (!visited[i] && (u == -1 || dist[i] < dist[u])) {
u = i;
}
}
visited[u] = 1; //表示这个顶点已经被访问过
//更新相邻顶点的距离和前驱结点
for (int j = 1; j <= G.vexnum; j++) { //通过判断新加了一个顶点后,能不能通过这个顶点使原来的距离更近,这是算法核心
if (!visited[j] && G.arcs[u][j] != INT_MAX && dist[u] != INT_MAX && dist[u] + G.arcs[u][j] < dist[j]) {
dist[j] = dist[u] + G.arcs[u][j];
prev[j] = u;
}
}
}
//检查是否存在路径
if (dist[end] == INT_MAX) {
printf("从%s 到 %s 不存在路径\n", G.vexs[start].name, G.vexs[end].name);
}
else {
printf("从%s 到 %s 的最短路径长度为%d,路径为:", G.vexs[start].name, G.vexs[end].name, dist[end]);
PrintPath(G,prev, start, end);
printf("\n");
}
}