题目要求(校园导航):
设计要求:设计某某大学的平面图,至少包括10个以上的场所,每两个场所间可以有不同的路,且路长也可能不同,找出从任意场所到达另一场所的最佳路径(最短路径)。
基本要求:
(1)设计校园平面图,在校园景点选10个左右景点。以图中顶点表示校园内各景点,存放景点名称、代号、简介等信息;以边表示路径,存放路径长度等有关信息。
(2)为来访客人提供图中任意景点相关信息的查询。
(3)为来访客人提供任意景点的问路查询,即查询任意两个景点之间的一条最短路径。
实现提示:一般情况下,校园的道路是双向通行的,可设计校园平面图是一个无向网。顶点和边均含有相关信息。
解题思路和大纲
首先读完题目,我们能清晰的了解到他所用到的数据结构和算法,那么不考虑其他部分,核心算法的选择是一个重要的问题。我们可以了解到,求解每一对顶点之间的最短路径有两种方法:
其一是分别以图中的每个顶点为源点共调用n次迪杰斯特拉算法;
其二是采用下面介绍的弗洛伊德(Floyd)算法。
两种算法的时间复杂度均为0(n3),但后者形式上较简单,弗洛伊德算法仍然使用带权的邻接矩阵arcs来表示。不难看出,此处我们小组选择的是弗洛伊德算法,简单粗暴,一次性算出任意两个顶点的最短距离。为什么选择弗洛伊德算法,第一个原因是大家可能首选迪杰斯特拉算法,为了新颖,我们组选择了弗洛伊德算法。第二个原因就是上面说的,简单粗暴。
那么,万事开头难。其他部分怎么开始,怎么解决,同样是难题。接下来我们小组的做法是:
1从高德地图采集地点信息,建立无向网(如何建立)
2如何储存顶点信息、边的信息(顶点,边的定义)
3如何建立图信息(联系点和边)
4邻接表?邻接矩阵?(由核心算法决定)
5.菜单页面(如何设计)
6文件读取
7核心算法选择(已解决)
此处我只介绍核心算法部分。MAX_VERTICES为点的总数。即循环的限制条件为顶的总数。
// 弗洛伊德算法主循环
for (k = 0; k < MAX_VERTICES; k++) {
for (i = 0; i < MAX_VERTICES; i++) {
for (j = 0; j < MAX_VERTICES; j++) {
if (Edg[i][j] > (Edg[i][k] + Edg[k][j])) {
Edg[i][j] = (Edg[i][k] + Edg[k][j]);//k为中间地点
path[i][j] = k; // 记录任意两地点之间的中间点
}
}
}
}
核心算法部分还有一个关键问题需要解决,如何输出最短路径。从核心代码部分,我们可以看出顶点i到顶点j之间可能有一个中间点k,也可能有两个,三个,或者更多。那么我们如何解决中间点k覆盖了顶点i到顶点j之间的点。其实很容易解决,如果能理解path[i][j]的关键作用,问题其实很简单。因为path数组存放了任意两点间的中间点。只需递归调用函数即可。
//递归显示最短路径
void show_ShortestPathRecursive(CampusMap* map, int i, int j, int intermediate)
{
if (intermediate == -1) {
printf("----%s", map->vertices[j].name);//打印终点
} else {
show_ShortestPathRecursive(map, i, intermediate, path[i][intermediate]);//递归i和intermediate点之间的中间点
show_ShortestPathRecursive(map, intermediate, j, path[intermediate][j]);//递归intermediate和j点之间的中间点
}
}
当然除了核心算法部分,需要解决的问题有很多。其余部分不在解释,最后附上源代码,仅供参考。欢迎大家提意见指正,相互学习。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_VERTICES 10//最大地点
#define INF 99999//无穷大
//地点结构体
typedef struct {
char name[50];// 地点名字
char description[200];//地点简介
}VertexInfo;
//无向网结构体
typedef struct {
VertexInfo vertices[MAX_VERTICES];//地点结构体数组
int adjacencyMatrix[MAX_VERTICES][MAX_VERTICES];//邻接矩阵
int numVertices;//地点个数
} CampusMap;
//全局变量方便函数直接使用、引用
CampusMap campus;
int path[MAX_VERTICES][MAX_VERTICES];
int Edg[MAX_VERTICES][MAX_VERTICES];
//初始化无向网
void initCampusMap(CampusMap* map) {
map->numVertices = 0;
for (int i = 0; i < MAX_VERTICES; ++i) {
for (int j = 0; j < MAX_VERTICES; ++j) {
if (i == j)
{//自身到达自身为0
map->adjacencyMatrix[i][j] = 0;
}
else
{
map->adjacencyMatrix[i][j] = INF;
}
}
}
}
//添加地点
void addVertex(CampusMap* map, char name[], char description[]) {
if (map->numVertices < MAX_VERTICES) {
strcpy(map->vertices[map->numVertices].name, name);//地点名字
strcpy(map->vertices[map->numVertices].description, description);//地点简介
map->numVertices++;
}
else
{//报错提醒
printf("Error: Too many vertices\n");
}
}
//添加边
void addEdge(CampusMap* map, int start, int end, int length) {
if (start >= 0 && start <map->numVertices && end >= 0 && end < map->numVertices) {
//无向边
map->adjacencyMatrix[start][end] = length;
map->adjacencyMatrix[end][start] = length;
}
else
{//报错提醒
printf("Error: Invalid vertex indices\n");
}
}
//以上是读取文件(点边图的初始化)
//输出各地点名称
void print_address(CampusMap* map)
{
int i = 0;
for (i = 0; i < MAX_VERTICES; i++)
{
printf("%d--%s\t", i+1, map->vertices[i].name);
}
printf("\n\n");
}
//打印地图
void Creat_maps()
{
printf(" ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━东北门━━━━━━┓ \n");
printf(" ┃ ┃ \n");
printf("【某某某某某某大学】| 二区篮球场 ┃ \n");
printf(" ┃ o ┃ \n");
printf(" ┃ 三峡大坝岩心 ┃ \n");
printf(" ┃ o ┃ \n");
printf(" ┏━━━━━━━━━ o 生活广场 ┃ \n");
printf(" ┃ ┃ \n");
printf(" ┃ 二区餐厅 ┃ \n");
printf(" ┃ o 第一报告厅 o 文体会堂 \n");
printf(" ┃ o \n");
printf(" ┃ 校医院 \n");
printf(" 西门 o \n");
printf(" ┃ 近邻宝 \n");
printf(" ┃ o ┃ \n");
printf(" ┃ \n");
printf(" ┃ o图书馆 \n");
printf(" ┃ \n");
printf(" ┃ \n");
printf(" 信息工程学院楼 ┃ \n");
printf(" o \n") ;
printf(" \n") ;
printf(" ┗ ━━━━━━━━━━━━━━━ ━━━━━━━━━━━━━━━南大门━ \n\n");
}
//遍历地点,查找查询地点的简介。
void Search(CampusMap* map,int a)
{
printf("%s:%s\n",map->vertices[a].name,map->vertices[a].description);
}
// 弗洛伊德算法求两点最小权值
void floyd(CampusMap* map)
{
int i, j, k;
// 初始化路径矩阵、边矩阵
for (i = 0; i < MAX_VERTICES; i++) {
for (j = 0; j < MAX_VERTICES; j++)
{
Edg[i][j]=map->adjacencyMatrix[i][j]; //初始化Edg矩阵
path[i][j] = -1; // 初始路径为空
}
}
// 弗洛伊德算法主循环
for (k = 0; k < MAX_VERTICES; k++) {
for (i = 0; i < MAX_VERTICES; i++) {
for (j = 0; j < MAX_VERTICES; j++) {
if (Edg[i][j] > (Edg[i][k] + Edg[k][j])) {
Edg[i][j] = (Edg[i][k] + Edg[k][j]);//k为中间地点
path[i][j] = k; // 记录任意两地点之间的中间点
}
}
}
}
}
// 递归显示最短路径
void show_ShortestPathRecursive(CampusMap* map, int i, int j, int intermediate)
{
if (intermediate == -1) {
printf("----%s", map->vertices[j].name);//打印终点
} else {
show_ShortestPathRecursive(map, i, intermediate, path[i][intermediate]);//递归i和intermediate点之间的中间点
show_ShortestPathRecursive(map, intermediate, j, path[intermediate][j]);//递归intermediate和j点之间的中间点
}
}
// 显示最短路径
void show_ShortestPath(CampusMap* map, int i, int j) {
printf("从 %s 到 %s 的最短路径为:\n", map->vertices[i].name, map->vertices[j].name);
printf("%s", map->vertices[i].name);//打印起点
if (path[i][j] != -1)
{
show_ShortestPathRecursive(map, i, j, path[i][j]);//调用函数show_ShortestPathRecursive
}
printf("\n最短距离为:%d米。\n", Edg[i][j]);//最短距离
}
//打印最短距离邻接矩阵
void print_adjacencyMatrix()
{
int i=0;int j=0;
for(i=0;i<MAX_VERTICES;i++)
{
for(j=0;j<MAX_VERTICES;j++)
printf("%d ",Edg[i][j]);
printf("\n");
}
}
//主函数
int main() {
initCampusMap(&campus);
//文件读取数据
FILE *file;
file = fopen("shujujiegou.txt", "r");//打开文件
if (file == NULL) {
printf("无法打开文件。\n");
return 1;
}
// 初始化点
int Vertexnum;
fscanf(file, "%d", &Vertexnum); //输入总顶点数
for (int i = 0; i < Vertexnum; i++) {
char address[50];//名字
char dercription[200];//介绍
fscanf(file, "%s %s", address, dercription);//文件读入
addVertex(&campus, address, dercription);//调用添加点addVertex函数
}
// 初始化边
int Edgnum;
fscanf(file, "%d", &Edgnum);//输入总边数
for (int i = 0; i < Edgnum; i++) {
int start, end, length;//起点、终点、距离
fscanf(file, "%d %d %d", &start, &end, &length);//文件读入
addEdge(&campus, start, end, length);//调用添加边addEdge函数
}
fclose(file);//关闭文件
//调用Floyd函数
floyd(&campus);
//菜单页面
printf("\t\t\t 功能菜单\n");
printf("--------------------------------------------------------------------------------\n");
printf("\t\t<1> 查看校园地图请输入1 \t\t\n");
printf("\t\t<2> 查看校园各地点代号请输入2 \t\t\n");
printf("\t\t<3> 最短路径查询请输入3 \t\t\n");
printf("\t\t<4> 查看存放最短路径的邻接矩阵请输入4 \t\t\n");
printf("\t\t<5> 退出系统请输入5 \t\t\n");
printf("------------------------------------------------------------------------------\n");
//遍历地点
print_address(&campus);
//功能菜单
int choice;
do {
printf("请输入对应功能的数字:\n");
scanf("%d", &choice);
switch (choice) {
case 1:
printf("地图如下:\n");
Creat_maps ();//打印地图
break;
case 2:
int value;
printf("请输入你要查询的地址:\n");
scanf("%d", &value);
Search(&campus, value-1);//调用查找函数,查找地点相应简介。
break;
case 3:
printf("请用户输入您要查询的路线(地点对应的序号):\n");
int a_start, a_end;
scanf("%d %d", &a_start, &a_end);
show_ShortestPath(&campus, a_start-1 , a_end-1 );//调用显示最短路径函数
break;
case 4:
printf("打印最短距离的邻接矩阵:\n");
print_adjacencyMatrix();//调用遍历最短路径矩阵
break;
case 5:
printf("退出菜单\n");
break;
default:
printf("无效的选择,请重新输入\n");
}
} while (choice != 5);
return 0;
}