第一章 概述
1.背景和意义
大学校园占比面积大,每年的新生在来到之后难免会遇到人生地不熟的尴尬境地,对于枯燥的地图很多人也不会看。那么设计一个简单的新生导游小程序让新同学在边玩的过程中认识校园就显得尤为重要。
2.系统任务和功能
用无向图表示山东理工大学的校园景点平面图,图中顶点存放景点的编号、名称、简介等信息,图中的边表示景点间的道路,存放路径长度等信息。要求能够回答刚刚入校的新生有关景点介绍、游览路径等问题。
3.功能:
(1)查询各景点的相关信息;
(2)提供任意景点之间的路径查询,能够提供两个顶点之间的所有路径信息;
(3)能够提供满足用户需求的任意两个景点间的最优路径查询服务(如距离最短、时间最少、换乘最少等)。
(4)能够提供特定约束条件下的最优访问路线(如单位时间内、限定步行距离范围内、限定交通工具下等)。
(5)图的信息要保存到文件中,并能够实现文件信息的修订。
(6)增加、删除、更新有关景点和道路的信息。
4.数据结构和算法
邻接矩阵和邻接表,深度优先搜索(DFS),弗洛伊德
5.创新点和特色
(1)本程序采用功能主界面模式,分为管理员模式与游客模式,可根据不同模式实现不同功能。
(2) 管理员界面与游客界面自动切换;
(3) 弗洛伊德算法路径的长度可自动计算;
(4) 出行方式多样,分别是步行、共享单车以及小公交;
(5) 用三维数组判断可达性;
(6) 根据高德地图形成校园仿真图;
(7) 更删改查之后表格信息不会覆盖,自动更改;
(8) 文件的信息可以自动导出excel表格;
第二章 概要设计
- 主程序流程(各函数之间的调用关系):
Main();//主函数
void create(Graph *G ) //构造图的矩阵存储
Vertex GetVex(Graph *G, int v) //任意景点的介绍
void PutVertex(Graph *G,int v) //修改景点信息
void InsertVertex (Graph *G,Vertex v) //插入景点
void DeleteVertex(Graph *G, int a) //删除景点
void InsertArc(Graph *G,int v, int w) //增加路径
void DeleteArc(Graph*G,int v,int w) //删除路径
void PrintPath()//输出所有路径的代码
void DFS(Graph *G,int now,int n) //所有路径搜索函数
void Floydshort(Graph *G) //查询经过任意两景点间的最短时间
void Floyd(Graph *G) //查询任意两景点间的最短路径
int show()//管理员界面显示
int show_1()//游客界面显示
void show0(Graph *G)//现实结点名称和编号
void show3()//输出校园仿真图
2.抽象数据类型定义:
ADT Graph{
数据对象V:V具有相同特性的数组元素的集合,称为点集。
数据关系R:
R={VR}
VR={<v,w>|v,w ϵV且P(v,w),<v,w>表示从v到w的弧,谓词P(v,w)定义了弧<v,w>的意义或信息。}
第三章 详细设计
1.图的结构
typedef struct //点的结构
{
char name[20];
char features[100];
} Vertex;
//矩阵结构体
typedef struct //图的结构
{
Vertex vexs[100];
int edges[500][500];
int n,e;//点和边数
} Graph;
2.各个函数的伪代码:
定义全局变量
int block[105];
int STA[105],top;
主函数main
{
show0(&G);//显示初始界面
if(a==111)//管理员界面显示
{
if(data==1)//查询景点介绍
else if(data==2)//修改景点信息
else if(data==3)//添加景点
else if(data==4)//删除景点
else if(data==5)//添加道路
else if(data==6)//删除道路
else if(data==7)//校园仿真图
else if(data==8)//回到游客界面
}
else(a==222)//游客界面显示
{
if(data==1)查询景点信息
else if(data==2)查询最短时间路径
else if(data==3)查询最短长度路径
else if(data==4)校园仿真图
else if(data==6)退出系统
}
}
//初始化图的矩阵存储(读取文件)
void InitializeG(Graph* G) {
MyFileIO f;
vector<vector<string> > ver_strArray = f.readFile(verInfo);//读取节点信息
vector<vector<string> > road_strArray = f.readFile(roadInfo);//读取道路信息
for (int i = 0; i < ver_strArray.size(); i++)//节点信息初始化
{
strcpy(G->vexs[i].name, ver_strArray[i][1].c_str());
strcpy(G->vexs[i].features, ver_strArray[i][2].c_str());
}
G->n = ver_strArray.size();//初始化节点个数
for (int i = 0; i < 50; i++)
for (int j = 0; j < 50; j++)
{
G->edges[i][j] = G->edges[j][i]= INF;
}
//初始化道路信息
G->e = 0;
for (int i = 0; i < road_strArray.size(); i++)
for (int j = i; j < road_strArray[i].size(); j++)
{
G->edges[i][j] = G->edges[j][i]= stoi(road_strArray[i][j]);
if (G->edges[i][j] != 0 && G->edges[i][j] != INF)
G->e++;
}
}
//遍历景点
int ergodic(Graph* G, int v)
{
int i;
for (i = 0; i < G->n; i++)
{
if (i == v)
return v;
}
return -1;
}
void display(Graph* G)
{
int k = -1;
int i, j;
printf("\n\n");
for (i = 0; i <= G->n; i++) {
printf("\n");
for (j = 0; j <= G->n; j++) {
if (G->time[i][j] < INF) {
printf("%d ", G->time[i][j]);
}
else if (i == j)
{
printf(" 0 ");
}
else {
printf(" ∞ ");
}
}
}
printf("\n\n");
}
//任意景点的介绍
Vertex GetVex(Graph* G, int v)
{
int j;
j = ergodic(G, v);
if (j == -1)
{
printf(" 无此景点\n\n\n\n");
}
else
{
printf("%s", G->vexs[v].name);
Vertex a = G->vexs[j];
return a;
}
}
int block[105];
int STA[105], top;
//修改景点信息
void PutVertex(Graph* G, int v)
{
int j;
j = ergodic(G, v);
if (j == -1)
printf("无景点\n");
else
{
char s[500];
printf("\n");
printf(" 输入修改后的景点信息:\n");
printf(" ");
//scanf("%s", s);
cin >> s;
strcpy(G->vexs[j].features, s);
printf("\n");
printf(" 修改完成\n");
}
}
//插入景点
void InsertVertex(Graph* G)
{
Vertex v;
char str_name[100], str_info[100];
int edge, a[100], i, a1[100], b;
printf(" 请输入景点名称:\n\n");
printf(" ");
scanf("%s", str_name,20);
strcpy(v.name, str_name);
printf("\n");
printf(" 请输入景点介绍:\n\n");
printf(" ");
scanf("%s", str_info,100);
strcpy(v.features, str_info);
printf("\n");
printf(" 请输入新增景点的边数:\n\n");
printf(" ");
scanf("%d", &edge);
printf("\n");
printf(" 请输入与新增景点连通的景点编号以及路径长度:\n\n");
G->e += edge;
G->n = G->n + 1;
G->vexs[G->n - 1] = v;
for (i = 1; i <= G->n; i++)
G->edges[G->n - 1][i] = G->edges[i][G->n - 1] = INF;//初始化
G->edges[G->n - 1][G->n - 1] = 0;//自身到自身为0
for (i = 1; i <= edge; i++)
{
printf(" ");
scanf("%d %d", &a1[i], &b);//编号和长度
a[i] = ergodic(G, a1[i]);
G->edges[G->n - 1][a[i]] = G->edges[a[i]][G->n - 1] = b;
printf("\n");
}
}
//删除景点
void DeleteVertex(Graph* G, int a)
{
int h, b = 0, i;
h = ergodic(G, a);
if (h == -1)
{
printf(" 无此景点\n\n\n\n\n");
}
else
{
//节点迁移
for (int k = h; k < G->n - 1; k++) {
strcpy(G->vexs[k].features, G->vexs[k + 1].features);
strcpy(G->vexs[k].name, G->vexs[k + 1].features);
}
//列数量计算
for (i = 0; i < G->n; i++)
{
if (G->edges[h][i] != INF && G->edges[h][i] != 0)
{
//G->edges[h][i] = G->edges[i][h] = INF;
G->e--;
}
}
//cout <<"--------边数--------"<< G->e << endl;
//数组整理:行、列迁移
//行迁移
for (int k = h; k < G->n - 1; k++) {
for (int v = 0; v < G->n; v++) {
G->edges[k][v] = G->edges[k + 1][v];
}
}
//列迁移
for (int k = h; k < G->n - 1; k++) {
for (int v = 0; v < G->n; v++) {
G->edges[v][k] = G->edges[v][k + 1];
}
}
G->n--;//节点数量计算
}
}
//增加路径
void InsertArc(Graph* G, int v, int w)
{
int length;
printf(" 请输入新加道路的长度:\n\n");
printf(" ");
scanf("%d", &length);
int a = ergodic(G, v);
int b = ergodic(G, w);
G->edges[a][b] = G->edges[b][a] = length;
}
//删除路径
void DeleteArc(Graph* G, int v, int w)
{
int a = ergodic(G, v);
int b = ergodic(G, w);
if (a == -1 || b == -1)
{
printf(" 无此景点\n\n\n\n\n");
}
else
G->edges[a][b] = G->edges[b][a] = INF;
}
//输出所有路径的代码
void PrintPath()
{
printf("%d", STA[0]);
for (int i = 1; i <= top; ++i)
printf("->%d", STA[i]);
printf("\n");
}
//所有路径搜索函数
void DFS(Graph* G, int now, int n)
{
block[now] = 1;
STA[++top] = now;
if (now == n) PrintPath();
for (int i = 0; i < G->n; i++)
if (block[i] == 0 && G->edges[now][i] < INF)
DFS(G, i, n);
top--;
block[now] = 0;
}
//根据地点名确定地点序号
int Locate(Graph* G, char name[])
{
int i;
for (i = 0; i < G->n; i++) {
//图中含有该景点,找到其序号
if (!strcmp(G->vexs[i].name, name))
return i;
}
return -1;
}
//查询经过任意两景点间的最短时间
void Floydshort(Graph* G)
{
int v, u, i, w, k, j, flag = 1, p[20][20][20], D[20][20];
for (v = 0; v < G->n; v++)
for (w = 0; w < G->n; w++)
{
D[v][w] = G->edges[v][w];
for (u = 0; u < G->n; u++)
p[v][w][u] = 0;
if (D[v][w] < INF)
{
p[v][w][v] = 1;
p[v][w][w] = 1;
}
}
for (u = 0; u < G->n; u++)//中转点
for (v = 0; v < G->n; v++)//源点
for (w = 0; w < G->n; w++)//终点
if (D[v][u] + D[u][w] < D[v][w])//不满足三角不等式就更新距离
{
D[v][w] = D[v][u] + D[u][w];
for (i = 0; i < G->n; i++)
p[v][w][i] = p[v][u][i] || p[u][w][i];
}
int da, v1 = 1, v2 = 3, v3 = 6, ab;
while (flag)
{
printf("请输入出发点和目的地的编号:");
scanf("%d %d", &k, &j);
if (k<0 || k>G->n || j<0 || j>G->n)//判断输入是否合法
{
printf("景点编号不存在!请重新输入出发点和目的地的编号:");
scanf("%d %d", &k, &j);
}
if (k >= 0 && k < G->n && j >= 0 && j < G->n)
flag = 0;
printf("请选择您的出行方式:1.步行 2.共享单车 3.小公交\n");
cin >> da;
if (da == 1)ab = v1;
else if (da == 2)ab = v2;
else if (da == 3)ab = v3;
else printf("没有该交通工具\n");
}
printf("%s", G->vexs[k].name);
for (u = 0; u < G->n; u++)
if (p[k][j][u] && k != u && j != u)
printf(" *** %s", G->vexs[u].name);//输出到某个点的最短路径
printf(" *** %s", G->vexs[j].name);
if (D[k][j] == INF)
printf(" 不存在通路!!!\n");
else printf(" 经过此总路线最少需要%dmin\n", D[k][j] / (ab * 60));
}
//查询任意两景点间的最短路径
void Floyd(Graph* G)
{
int v, u, i, w, k, j, flag = 1, p[20][20][20], D[20][20];
for (v = 0; v < G->n; v++)
for (w = 0; w < G->n; w++)
{
D[v][w] = G->edges[v][w];
for (u = 0; u < G->n; u++)
p[v][w][u] = 0;
if (D[v][w] < INF)
{
p[v][w][v] = 1;
p[v][w][w] = 1;
}
}
for (u = 0; u < G->n; u++)//中转点
for (v = 0; v < G->n; v++)//源点
for (w = 0; w < G->n; w++)//终点
if (D[v][u] + D[u][w] < D[v][w])//不满足三角不等式就更新距离
{
D[v][w] = D[v][u] + D[u][w];
for (i = 0; i < G->n; i++)
p[v][w][i] = p[v][u][i] || p[u][w][i];
}
while (flag)
{
printf("请输入出发点和目的地的编号:");
scanf("%d %d", &k, &j);
if (k<0 || k>G->n || j<0 || j>G->n)//判断输入是否合法
{
printf("景点编号不存在!请重新输入出发点和目的地的编号:");
scanf("%d %d", &k, &j);
}
if (k >= 0 && k < G->n && j >= 0 && j < G->n)
flag = 0;
}
printf("%s", G->vexs[k].name);
for (u = 0; u < G->n; u++)
if (p[k][j][u] && k != u && j != u)
printf(" *** %s", G->vexs[u].name);//输出到某个点的最短路径
printf(" *** %s", G->vexs[j].name);
if (D[k][j] == INF)
printf(" 不存在通路!!!\n");
else printf(" 总路线长%dm\n", D[k][j]);
}
int show()
{
printf(" 管理员页面 \n");
printf(" 1.景点介绍 \n\n");
printf(" 2.修改景点信息 \n\n");
printf(" 3.增加景点 \n\n");
printf(" 4.删除景点 \n\n");
printf(" 5.增加道路 \n\n");
printf(" 6.删除道路 \n\n");
printf(" 7.查看校园导航仿真图 \n\n");
printf(" 8.退出导航 \n\n");
printf("请选择相应的功能: ");
int data;
scanf("%d", &data);
return data;
}
int show_1()
{
printf(" 游客页面 \n");
printf(" 1.景点介绍 \n\n");
printf(" 2.查找任意两个景点所需的最短时间路径 \n\n");
printf(" 3.查找任意两个景点最短路径 \n\n");
printf(" 4.查看校园导航仿真图 \n\n");
printf(" 5.查询起点到终点的所有路径 \n\n");
printf(" 6.退出导航 \n\n");
printf("请选择相应的功能: ");
int data;
scanf("%d", &data);
return data;
}
void show0(Graph* G)
{
int i;
for (i = 0; i < G->n; i++)
{
printf(" %d->%s\n", i, G->vexs[i].name);
}
}
void show3()
{
printf(" |----------------学生宿舍区(9)-----------------| \n");
printf(" | | | \n");
printf(" | | | \n");
printf(" | 第三餐厅(7) | \n");
printf(" | / \ | \n");
printf(" | / \ | \n");
printf("计算机科学与技术学院(8)-----稷下湖景区(6) \ | \n");
printf(" | \\ \ | \n");
printf(" | \\ 第二体育场(3) | \n");
printf(" | \\ / \ | \n");
printf(" | \\ / \ | \n");
printf(" | \\ / \ | \n");
printf(" | \\ / \ | \n");
printf(" 逸夫图书馆(1)----第三教学楼(2) 大红炉(5) \n");
printf(" | \\ / | \n");
printf(" | \\ / | \n");
printf(" | 一号实验楼(4)------鸿远楼(10) | \n");
printf(" | | | \n");
printf(" | | | \n");
printf(" | | | \n");
printf(" | | | \n");
printf(" |--------------------------------学校南门(0)---| \n");
printf("\n\n");}
//将图信息保存到文件
void SaveFile(vector<vector<string> > c,Graph *G,int flag) {
MyFileIO f;
vector<string> s;//容器存放矩阵每一行的各个元素
c.clear();
//值为1表示只需修改景点信息,2表示只需修改道路信息,3表示需全部修改
if (flag == 1|| flag == 3) {
for (int i = 0; i < G->n; i++) {
s.clear();
s.push_back(to_string(i));
s.push_back(G->vexs[i].name);
s.push_back(G->vexs[i].features);
c.push_back(s);//矩阵一行为一个元素
}
f.writeFile(verInfo, c);
}
c.clear();
if (flag == 2|| flag == 3) {
for (int i = 0; i < G->n; i++) {
s.clear();
for (int j = 0; j < G->n; j++)
s.push_back(to_string(G->edges[i][j]));
c.push_back(s);
}
f.writeFile(roadInfo, c);
}
}
第四章 测试与运行
主界面:
管理员界面:
游客界面:
最短时间查询:
最短路径查询: