|
|
@ -63,8 +63,8 @@
|
|
|
|
### (1)输入和输出
|
|
|
|
### (1)输入和输出
|
|
|
|
|
|
|
|
|
|
|
|
输入城市编号,即可显示该城市的具体信息,最短路径等。
|
|
|
|
输入城市编号,即可显示该城市的具体信息,最短路径等。
|
|
|
|
输入:城市的编号,城市信息文件,城市之间的路径
|
|
|
|
输入:城市的编号,城市之间的路径
|
|
|
|
输出:城市的编号,名称以及简介,最短路径
|
|
|
|
输出:城市的编号,名称,最短路径
|
|
|
|
|
|
|
|
|
|
|
|
### (2)数据字典
|
|
|
|
### (2)数据字典
|
|
|
|
|
|
|
|
|
|
|
@ -121,26 +121,34 @@ short.h
|
|
|
|
功能:修改城市信息
|
|
|
|
功能:修改城市信息
|
|
|
|
输入:要修改的城市编号
|
|
|
|
输入:要修改的城市编号
|
|
|
|
输出:无
|
|
|
|
输出:无
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
### (6)路经查询功能
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
输入任意两个城市,输出这两个城市之间的最短路径(路径距离和具体的经过的城市名称)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
### (6)导航规划功能
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
系统可以根据用户输入的出发地和目的地,计算出最短路径,供用户导航。
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# 2. 系统设计
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## 2.1 概要设计
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
系统划分为几个模块,可以画模块图。
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
逐个说明每个模块的功能(输入、输出、做什么,这里不写怎么做)。
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# 2. 系统设计
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## 2.1 概要设计
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
查找数据模块:城市信息、道路连接信息、路径长度等数据,提供给其他模块作为输出。
|
|
|
|
|
|
|
|
最短路径查询模块:接收用户输入出发地和目的地,用Dijkstra算法计算最短路径,输出路径距离和路径序列。
|
|
|
|
|
|
|
|
数据修改模块:根据用户想要修改的城市信息,输入编号,修改此城市信息
|
|
|
|
|
|
|
|
删除数据模块:根据用户想要删除的城市,输入编号,删除该城市与其邻接点城市的所有信息。
|
|
|
|
|
|
|
|
增加数据模块:添加城市,添加编号,添加该城市的邻接点及路径信息。
|
|
|
|
|
|
|
|
DFS遍历输出:输出所有城市信息。
|
|
|
|
|
|
|
|
|
|
|
|
## 2.2 数据结构设计
|
|
|
|
## 2.2 数据结构设计
|
|
|
|
|
|
|
|
|
|
|
@ -156,7 +164,15 @@ struct MatGrath
|
|
|
|
ArcCell arc[M][M]; //在图结构体中调用边的结构体
|
|
|
|
ArcCell arc[M][M]; //在图结构体中调用边的结构体
|
|
|
|
};
|
|
|
|
};
|
|
|
|
```
|
|
|
|
```
|
|
|
|
|
|
|
|
例如,3个城市的网络,它的邻接矩阵可以表示为:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
青岛市 即墨区 胶州市
|
|
|
|
|
|
|
|
青岛市 0 51 41
|
|
|
|
|
|
|
|
即墨区 51 0 56
|
|
|
|
|
|
|
|
胶州市 41 56 0
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
###
|
|
|
|
###
|
|
|
|
|
|
|
|
|
|
|
@ -165,76 +181,6 @@ struct MatGrath
|
|
|
|
首先,分析对比几种可选的算法设计方案。如是否排序,广度优先或深度优先搜索等。给出每一种设计方案的特点(优势、不足)。然后,综合考虑各种因素(空间、时间、乃至团队成员的水平等),给出你的选择。
|
|
|
|
首先,分析对比几种可选的算法设计方案。如是否排序,广度优先或深度优先搜索等。给出每一种设计方案的特点(优势、不足)。然后,综合考虑各种因素(空间、时间、乃至团队成员的水平等),给出你的选择。
|
|
|
|
|
|
|
|
|
|
|
|
### (1)Dijkstra算法
|
|
|
|
### (1)Dijkstra算法
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
求最短路径Dijkstra算法的实现:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
设置Visited[]保存是否被标记,Dis[]保存用户起点到下标号的节点路径的长度,Father[]保存最短路径上的前一个节点。其他类似于Prim算法。
|
|
|
|
|
|
|
|
算法主体有三步:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1.更新:找到这样的两个顶点 即 i位置已经被访问 且j位置未被访问 且ij有连接,把(0,i)+(i,j)和(0,j)中的较小者赋给j,并且Father[j] = i即i作为j的前驱节点。
|
|
|
|
|
|
|
|
2.访问:每轮在更新Dis后 判断最小未被标记的顶点 对其访问(vis = 1;)
|
|
|
|
|
|
|
|
3.重复1、2 步骤直到Visited[]的值全为1为止。
|
|
|
|
|
|
|
|
然后是输出语句:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
输出用户输入位置到其他各个顶点的最短路径长度:
|
|
|
|
|
|
|
|
直接一个循环输出Dis数组的前n位即可
|
|
|
|
|
|
|
|
输出用户输入位置到其他各个顶点的最短路径对应的路线:
|
|
|
|
|
|
|
|
首先设置i从0到n的循环每次都将i赋值给VVV作为终点,v是用户输入的起点位置。
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
while (Father[vvv] != -1 && Father[vvv] != v)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
cout << Father[vvv] << "<--";
|
|
|
|
|
|
|
|
vvv = Father[vvv];
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
根据Father数组内存的节点的前驱关系输出路线。
|
|
|
|
|
|
|
|
以上是迪杰斯特拉算法的基础部分,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
在本程序中由于多次调用了此算法,为了减少重复的代码提高代码的复用性,于是在此算法中增加了一个接口,即一个switch case开关语句,调用迪杰斯特拉时需要在参数列表中给出需要进入的部分以实现不同的功能。
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
给出核心算法的设计,包括伪代码或流程图。多个核心算法,逐一列出。只列举解决问题的核心算法,重点讲清楚是如何解决问题的。
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# 3. 系统实现
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
采用C++、利用VSCode实现。
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
本程序首先是用户编辑界面,用户根据自己的需求编写地图,从而加入顶点的数组之中,创建的地图用邻接矩阵存储,在从主函数之中进行调用,实现对两个算法的调用。用户在输入顶点以及边的信息都会存储,在存储成功之后会提示用户存储成功,之后进入到菜单界面,菜单界面提供两种选择口合,分别可以调运Dijkstra和Floyd算法调用之后输入相应的口令以及要查询的城市编号, 算法会根据邻接矩阵存储的地图进行计算,求出最短路径。在以后使用完系统后,可输入口合 0,系统会结束一切运算,退出程序。
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## 3.1 核心数据结构的实现
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
```cpp
|
|
|
|
|
|
|
|
struct ArcCell
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
int length;//边的长度,既两个地点之的长
|
|
|
|
|
|
|
|
int money;
|
|
|
|
|
|
|
|
}; //定义边的类型
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
struct VertexType
|
|
|
|
|
|
|
|
{ int no; //顶点的编号
|
|
|
|
|
|
|
|
char sight[10]; //地点
|
|
|
|
|
|
|
|
char introduction[100]; //地点的介绍
|
|
|
|
|
|
|
|
}; //定义顶点的类型
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
struct MatGrath
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
int vexnum; //顶点数
|
|
|
|
|
|
|
|
int arcnum; //边数
|
|
|
|
|
|
|
|
VertexType vexs[NO]; //在图结构体中调用点的结构体
|
|
|
|
|
|
|
|
ArcCell arc[NO][NO]; //在图结构体中调用边的结构体
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
```
|
|
|
|
```
|
|
|
|
int Ppath2(MatGrath &G,int path[],int i,int v) //前向递归查找路径上的顶点
|
|
|
|
int Ppath2(MatGrath &G,int path[],int i,int v) //前向递归查找路径上的顶点
|
|
|
|
{
|
|
|
|
{
|
|
|
@ -309,8 +255,74 @@ int Ppath1(MatGrath &G,int path[][MAXV],int v,int w) //前向递归查找
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
求最短路径Dijkstra算法的实现:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
设置Visited[]保存是否被标记,Dis[]保存用户起点到下标号的节点路径的长度,Father[]保存最短路径上的前一个节点。其他类似于Prim算法。
|
|
|
|
|
|
|
|
算法主体有三步:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1.更新:找到这样的两个顶点 即 i位置已经被访问 且j位置未被访问 且ij有连接,把(0,i)+(i,j)和(0,j)中的较小者赋给j,并且Father[j] = i即i作为j的前驱节点。
|
|
|
|
|
|
|
|
2.访问:每轮在更新Dis后 判断最小未被标记的顶点 对其访问(vis = 1;)
|
|
|
|
|
|
|
|
3.重复1、2 步骤直到Visited[]的值全为1为止。
|
|
|
|
|
|
|
|
然后是输出语句:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
输出用户输入位置到其他各个顶点的最短路径长度:
|
|
|
|
|
|
|
|
直接一个循环输出Dis数组的前n位即可
|
|
|
|
|
|
|
|
输出用户输入位置到其他各个顶点的最短路径对应的路线:
|
|
|
|
|
|
|
|
首先设置i从0到n的循环每次都将i赋值给VVV作为终点,v是用户输入的起点位置。
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
while (Father[vvv] != -1 && Father[vvv] != v)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
cout << Father[vvv] << "<--";
|
|
|
|
|
|
|
|
vvv = Father[vvv];
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
根据Father数组内存的节点的前驱关系输出路线。
|
|
|
|
|
|
|
|
以上是迪杰斯特拉算法的基础部分,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
在本程序中由于多次调用了此算法,为了减少重复的代码提高代码的复用性,于是在此算法中增加了一个接口,即一个switch case开关语句,调用迪杰斯特拉时需要在参数列表中给出需要进入的部分以实现不同的功能。
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
给出核心算法的设计,包括伪代码或流程图。多个核心算法,逐一列出。只列举解决问题的核心算法,重点讲清楚是如何解决问题的。
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# 3. 系统实现
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
采用C++、利用VSCode实现。
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
本程序首先是用户编辑界面,用户根据自己的需求编写地图,从而加入顶点的数组之中,创建的地图用邻接矩阵存储,在从主函数之中进行调用,实现对两个算法的调用。用户在输入顶点以及边的信息都会存储,在存储成功之后会提示用户存储成功,之后进入到菜单界面,菜单界面提供两种选择口合,分别可以调运Dijkstra和Floyd算法调用之后输入相应的口令以及要查询的城市编号, 算法会根据邻接矩阵存储的地图进行计算,求出最短路径。在以后使用完系统后,可输入口合 0,系统会结束一切运算,退出程序。
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## 3.1 核心数据结构的实现
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
```cpp
|
|
|
|
|
|
|
|
struct ArcCell
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
int length;//边的长度,既两个地点之的长
|
|
|
|
|
|
|
|
int money;
|
|
|
|
|
|
|
|
}; //定义边的类型
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
struct VertexType
|
|
|
|
|
|
|
|
{ int no; //顶点的编号
|
|
|
|
|
|
|
|
char sight[10]; //地点
|
|
|
|
|
|
|
|
char introduction[100]; //地点的介绍
|
|
|
|
|
|
|
|
}; //定义顶点的类型
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
struct MatGrath
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
int vexnum; //顶点数
|
|
|
|
|
|
|
|
int arcnum; //边数
|
|
|
|
|
|
|
|
VertexType vexs[NO]; //在图结构体中调用点的结构体
|
|
|
|
|
|
|
|
ArcCell arc[NO][NO]; //在图结构体中调用边的结构体
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -395,68 +407,6 @@ int Ppath2(MatGrath &G,int path[],int i,int v) //前向递归查找路径上的
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int danyuan(MatGrath &G,int v)//求两点之间的最短路径
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
int dist[MAXV],path[MAXV];
|
|
|
|
|
|
|
|
int s[MAXV];
|
|
|
|
|
|
|
|
int mindis,i,j,u;
|
|
|
|
|
|
|
|
for (i=1; i<=G.vexnum; i++)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
dist[i]=G.arc[v][i].length; //距离初始化
|
|
|
|
|
|
|
|
s[i]=0; //s[]置空
|
|
|
|
|
|
|
|
if (G.arc[v][i].length<INF) //路径初始化
|
|
|
|
|
|
|
|
path[i]=v;
|
|
|
|
|
|
|
|
else
|
|
|
|
|
|
|
|
path[i]=-1;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
s[v]=1;
|
|
|
|
|
|
|
|
path[v]=0; //源点编号v放入s中
|
|
|
|
|
|
|
|
for (i=1; i<=G.vexnum; i++) //循环直到所有顶点的最短路径都求出
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
mindis=INF; //mindis置最小长度初值
|
|
|
|
|
|
|
|
for (j=1; j<=G.vexnum; j++) //选取不在s中且具有最小距离的顶点u
|
|
|
|
|
|
|
|
if (s[j]==0 && dist[j]<mindis)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
u=j;
|
|
|
|
|
|
|
|
mindis=dist[j];
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
s[u]=1; //顶点u加入s中
|
|
|
|
|
|
|
|
for (j=1; j<=G.vexnum; j++) //修改不在s中的顶点的距离
|
|
|
|
|
|
|
|
if (s[j]==0)
|
|
|
|
|
|
|
|
if (G.arc[u][j].length<INF && dist[u]+G.arc[u][j].length<dist[j])
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
dist[j]=dist[u]+G.arc[u][j].length;
|
|
|
|
|
|
|
|
path[j]=u;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for(i=1; i<=G.vexnum; i++)
|
|
|
|
|
|
|
|
if (s[i]==1&&v!=i)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
printf(" 从%s到%s的最短路径长度为:%d米\t路径为:",G.vexs[v].sight,G.vexs[i].sight,dist[i]);
|
|
|
|
|
|
|
|
printf("%s->",G.vexs[v].sight); //输出路径上的起点
|
|
|
|
|
|
|
|
Ppath2(G,path,i,v); //输出路径上的中间点
|
|
|
|
|
|
|
|
printf("%s\n",G.vexs[i].sight); //输出路径上的终点
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int Ppath1(MatGrath &G,int path[][MAXV],int v,int w) //前向递归查找路径上的顶点
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
int k;
|
|
|
|
|
|
|
|
k=path[v][w];
|
|
|
|
|
|
|
|
if (k==-1) return 0; //找到了起点则返回
|
|
|
|
|
|
|
|
Ppath1(G,path,v,k); //找顶点i的前一个顶点k
|
|
|
|
|
|
|
|
printf("%s->",G.vexs[k].sight);
|
|
|
|
|
|
|
|
Ppath1(G,path,k,w); //找顶点k的前一个顶点j
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|