C++数据结构小项目——校园地图设计及其应用

内容简介:

这是一个我大二最近的数据结构课设题目,拿出来和大家分享分享,如有错误之处劳烦大家帮忙更正呀

题目如下:

C++数据结构小项目——校园地图设计及其应用_第1张图片

需求功能分析:

  1. 查询任一地点的基本信息
  2. 查询任意两个地点之间的最短路径及其长度

特别注意:我并没有将图中“测试数据”一栏中的第一和第二点加入需求中,我的理由是:程序的对象是用户,类似于初始化地图信息的操作应该由设计者本身去完成,而不是交给用户,这极大影响了用户体验;如果我一开始就按照题目上的要求进行设计的话,完成之后拿给同学进行测试的话,那么对方还需要给地图进行初始化,这要输入很多信息,可能对方刚输入一半就放弃了你的测试

逻辑结构分析:

  • 我们知道,校园中的道路往往都是双向通行的,所以我们可以将校园的各个地点看做是一个一个顶点,而两地点之间的道路看做是两个顶点之间的边,那么道路的长度就是边的权值
  • 由此我们不难得出校园地图的逻辑结构很显然是一个无向网,并且是加权无向网

物理存储结构设计:

  • 根据上面的逻辑结构分析,我们知道校园地图抽象为一个加权无向网,那么对于无向网而言,在已有的数据结构中,邻接矩阵是最为适合无向网的存储工作的,邻接矩阵十分便于访问图中的各个顶点,所以整个地图地点之间的关联关系和权值均存储在邻接矩阵中,说通俗点也就是个二维数组
  • 因为各个地点还有各自的名称、代号和简介,那么就将名称、代号和简介封装成为一个结构体Vex,Vex中具有名称、代号和简介三个成员变量,用Vex[vexsnum]结构体数组存储地图中所有地点的基本信息(这里的vexnum就是顶点个数),下文将Vex[vexsnum]这个数组简称为顶点信息数组
  • 最后再将顶点信息数组和邻接矩阵封装为一个结构体Graph,Graph中具有顶点信息数组和邻接矩阵两个成员。至此,对校园地图各个地点的信息和关联关系的存储问题均已解决
  • 在设计上面两个结构体之前,还需要预编译时定义两个常量,分别是顶点个数和边数,作为顶点信息数组和邻接矩阵的个数参数,这里给出Vex和Graph结构体的基本实现代码
#define MAXVEX  15          //最大顶点数,应由用户定义
#define NUMBER  22          //最大边数,应由用户定义
typedef string VertexName;             //顶点名称应由用户定义
typedef char replace;                  //顶点代号应由用户定义
typedef string info;                   //顶点简介应由用户定义
typedef struct {
	VertexName name; //地点名称
	replace ch; //地点代号
	info str; //地点简介
}Vexs;

typedef int EdgeType;                    //边上的权值类型应由用户定义
typedef struct {
	Vexs vexs[MAXVEX];                   //顶点信息数组
	EdgeType   arc[MAXVEX][MAXVEX];      //邻接矩阵,可看作边
	int numVertexes, numEdges;           //图中当前的顶点数和边数
}Graph;

特别注意:此处的typedef是对后面声明变量的“数据类型”起了一个别名,而这个别名就是“变量名”;比如typedef string info;这个语句,那么以后想用string类型来声明变量可以用别名来替代,例如:声明一个string类型的变量a,那么就可以用以下语句进行声明

//声明了一个string变量a
info a;

//等价于
string a;

算法设计:

1 . locates定位函数:
因为我们每一个地点是使用邻接矩阵而存储的,那么也就是说地点 A 对应邻接矩阵的行号列号均是 i ,这里的 i 是整数,而我们的地点名称为string类型所存储(此处请参考上文的Vex结构体声明),所以我们需要一个将string类型的地点名称可以转化为对应邻接矩阵上的整数行号和列号,这样子才能通过输入一个string类型的地点名称,通过转定位函数成换整型的行号和列号之后,才能方便地访问到邻接矩阵和顶点信息数组的对应信息。
这里给出locates函数的具体实现:

int locates(Graph *g, string ch) {
	int i = 0;
	for (i = 0; i < g->numVertexes; i++) {    //g->numVertexes为当前顶点数
		if (g->vexs[i].name == ch) {          //g->vexs[i].name为各个地点的名称
			break;
		}
	}
	if (i >= g->numVertexes) {
		return -1;
	}
	return i;
}

2 . createGraph创建地图无向网函数:(初始化)
这里的初始化函数将用预先准备好的校园地点和地点之间的关联关系来进行无向网的搭构任务,所以我准备了以下的信息

选择了15个广东理工学院校园地点,分别为:
振华楼、学院练车场、美食广场、春晖苑、学校游泳池、东区足球场、翔中堂、健身房、电工房、科技广场、
生活服务中心、桃李苑、图书馆、田径场、体育馆

设置了22条边关联:(这里的边是无向边,后面数字代表边的权)
振华楼 <->学院练车场 30
振华楼 <-> 美食广场 20
振华楼 <-> 春晖苑 25
振华楼 <-> 学校游泳池 50
学院练车场 <-> 美食广场 10
美食广场 <-> 春晖苑 5
学校游泳池 <-> 东区足球场 5
学校游泳池 <-> 翔中堂 30
东区足球场 <-> 春晖苑 50
东区足球场 <-> 健身房 35
翔中堂 <-> 健身房 7
翔中堂 <-> 电工房 20
健身房 <-> 电工房 15
电工房 <-> 科技广场 50
电工房 <-> 生活服务中心 100
科技广场 <-> 图书馆 35
科技广场 <-> 田径场 10
生活服务中心 <-> 图书馆 40

特别注意:上面我没有写代号和简介,是为了简洁文章长度,因为原理是和地点名称一样的,均用一个一维数组来进行存储

以下给出预先将上述信息存储好的代码片段

//事先把地图信息存储起来
//地点名称
VertexName vexname[15] = {
	"振华楼",
	"学院练车场",
	"美食广场",
	"春晖苑",
	"学校游泳池",
	"东区足球场",
	"翔中堂",
	"健身房",
	"电工房",
	"科技广场",
	"生活服务中心",
	"桃李苑",
	"图书馆",
	"田径场",
	"体育馆"
}; 
//地点代号
replace vexch[15] = {
	'Z','L','M','H','Y','Q','X','J','D','K','S','T','G','C','P'
};
//地点简介
info vexstr[15] = {
	"振华楼是信息技术学院学员的主要活动场所,内置教室数百间,室内硬件配置齐全,当为模范教学楼!",
	"学院练车场是与学校共同合作的一个校外商家,致力于为学生提供物美价廉的考驾照之旅,练车场位于校内,十分方便学生全天候练车!",
	"美食广场位于广东理工学院东区区域,与春晖苑相邻,其中店家琳琅满目,数不胜数,风格深受学生欢迎,是校园的一处好栖息地!",
	"春晖苑也被称之为东区食堂,与美食广场共同提供了学生们的日常餐饮生活,相对比与美食广场而言,春晖苑的装饰更为古风一些!",
	"学校游泳池位于学校的东区区域,泳池是室内的,所以在任何时间段都十分适合学生下水,当然夏日泳池的plmm才是不可多得的一道风景线!",
	"东区足球场,与学校游泳池相邻,有着良好的草地质感,给予运动员最舒适最安全的比赛场地,见证了无数广理足星的诞生!",
	"翔中堂也被称之为礼堂,翔中堂主要作为大型活动的场地,内部有覆盖全场的空调,观众席十分豪华,并且座椅多达两层楼之多!",
	"健身房位于礼堂的对角处,它是学生健身爱好者的天堂,内部硬件设施完善,安全设施齐全,如果店家可以免费让学生体验,再好不过了!",
	"电工房位于校园的中间部分,当学生们宿舍发生电路问题时,都可以求助电工房来进行修理,当然电工一般是十分忙绿的,毕竟只有一所电工房!",
	"科技广场是我校的重要场所,其广场里的科技之星雕塑更是我校的标示性建筑,寓意着师生不断创新,追求真理的科技之心!",
	"生活服务中心也被学生们称之为西区超市,因为其建筑和超市并无两样,除此之外,最大的特色就是网红街,试想,一个宁静的夜晚,在满是星星灯下的你和好朋友们畅聊着星辰大海,是多么惬意的事情!",
	"桃李苑位于西区区域,也称之为西区饭堂,不过其内部的装饰还是店家的款式,都比春晖苑要好一些!",
	"图书馆位于我校正门的正前方,正所谓一进门就得以闻其书香气,图书馆是追求知识的天堂,里面有着一群志同道合,向往远方的青年少女,一起为自己的未来而去奋斗!",
	"田径场是我校举行校运会的主要场地,有着400米的塑胶跑道,为西区的学生们提供了不可多得的课后锻炼场所!",
	"体育馆与田径场相邻,这是我校唯一的室内运动馆,内有一个篮球场和四个羽毛球场,如果能再多一些室内场地,那将十分深受学生们的欢迎!",
};


//初始化地点名称1
string loadingOne[22] = {
	"振华楼",
	"学院练车场",
	"美食广场",
	"振华楼",
	"振华楼",
	"振华楼",
	"学校游泳池",
	"东区足球场",
	"学校游泳池",
	"东区足球场",
	"翔中堂",
	"翔中堂",
	"健身房",
	"电工房",
	"电工房",
	"科技广场",
	"科技广场",
	"田径场",
	"图书馆",
	"图书馆",
	"桃李苑",
	"生活服务中心"
}; 
//初始化地点名称2
string loadingTwo[22] = {
	"学院练车场",
	"美食广场",
	"春晖苑",
	"美食广场",
	"春晖苑",
	"学校游泳池",
	"东区足球场",
	"春晖苑",
	"翔中堂",
	"健身房",
	"健身房",
	"电工房",
	"电工房",
	"科技广场",
	"生活服务中心",
	"图书馆",
	"田径场",
	"体育馆",
	"田径场",
	"桃李苑",
	"生活服务中心",
	"图书馆"
};
//初始化地点路程
int loadingNum[22] = {
	30,10,5,20,
	25,50,5,50,
	30,35,7,20,
	15,50,100,35,
	10,5,10,20,
	25,40
};

由此我们可以知道vexname、vexch、vexstr这三个数组的下标是一一对应的关系,即vexname[5]对应着vexch[5]和vexstr[5];并且不难看出loadingOne、loadingTwo、loadingNum这三个数组也是一一对应的关系,这种关系和前面三个数组的关系是一样的。

那么用于初始化的信息准备好了之后,就可以开始将校园地图抽象为无向网啦,这里给出createGraph函数的完整代码,此函数中调用了locates定位函数,不清楚locates定位函数的道友们可以回头去看看哦

#define INF 9999999    //定义一个无穷大数
void createGraph(Graph *g) {

	int i, j, k, w;
	//初始化顶点数和边数
	g->numVertexes = MAXVEX; //15个顶点
	g->numEdges= NUMBER;    //22条边
	
	//初始化广东理工学院地点名称、代号、简介,很明显地看出来下标是一一对应的关系
	 for (i = 0; i < g->numVertexes; i++) {
		 g->vexs[i].name = vexname[i];
		 g->vexs[i].ch = vexch[i];
		 g->vexs[i].str = vexstr[i];
	 }

	 //邻接矩阵初始化为每个单元为INF
	for (i = 0; i < g->numVertexes; i++) {
		for (j = 0; j < g->numVertexes; j++) {
			g->arc[i][j] = INF;
		}
	}
	cout << endl;

	//初始化地图的邻接矩阵,此处注意观察三个数组的下标一一对应关系
	for (k = 0; k < g->numEdges; k++) {
		string p, q;
		p = loadingOne[k];
		q = loadingTwo[k];
		w=loadingNum[k];
		int m = -1;
		int n = -1;
		m = locates(g, p);//若m等于-1,说明在矩阵中并没有定位到
		n = locates(g, q);
		if (m == -1 || n == -1) {
			cout << "there is no vertex." << endl; //显示输入有错信息
			return;
		}
		g->arc[m][n] = w;
		g->arc[n][m] = w; //因为是无向图,矩阵对称
	}
}

3 . printGraph函数:
这个函数的作用是打印整个校园的所有地点的基本信息,虽然需求中没有提到,但是现实生活中总会有想直接看整个校园地图的情况,所以为了更加贴切实际情况,我加了这么一个打印函数,这个函数也比较简单,遍历一遍Vex结构体数组并且输出信息就能实现,给出此函数代码段

//打印整个校园的所有地点的基本信息
void printGraph(Graph g) {
	int i;
	for (i = 0; i < g.numVertexes;i++){
		cout << "地点名称:" << g.vexs[i].name << endl;
		cout << "代号:"<< g.vexs[i].ch << endl;
		cout << "简介:" << g.vexs[i].str << endl;
		cout << endl;
	}
}

3 . find函数:
这个函数正是对应了课设需求一(查询任一地点的基本信息),这个函数和上面那个打印全部信息的函数多了一个地点比较的部分,实现起来也并不难,给出此函数代码段

//查询任一地点的基本信息
void find(Graph g,string ch){
	int i;
	for (i = 0; i < g.numVertexes; i++) {
		if (ch == g.vexs[i].name) {
			cout << "地点名称:" << g.vexs[i].name << endl;
			cout << "代号:" << g.vexs[i].ch << endl;
			cout << "简介:" << g.vexs[i].str << endl;
			cout << endl;
		}
	}
}

4 . shortestPath函数:
此函数的功能对应着课设需求二(查询任意两个地点之间的最短路径和长度),其实这个函数是可以根据已有的迪杰斯特拉算法进行改造的,我并没有深入去优化这个算法(但肯定是能优化的);这个函数还调用了另外一个函数,就是Dispath函数(用于输出最短路径与长度),因为输出这个操作单独放在shortestPath函数里面会显得十分冗余,所以我将输出的部分在shortestPath函数之前封装在了一起,给出以下代码段(代码注释较为清晰);道友们可以从shortestPath函数先看起,再到Dispath函数,这样更容易理解,这是整个程序的核心代码

//输出任一两点间的最短路径与长度
void Dispath(int dist[], int path[], int vv,int ee,Graph g)//输出从源点v出发的所有最短路径
{
	int i, j, k;
	int  d;//存放一条最短路径(逆向)及其顶点个数
	string apath[MAXVEX];
	for (i = 0; i < g.numVertexes; i++)//n为顶点个数,循环n次,输出源点到其余各个顶点的最短路径和路径长度
	{
		if (i == ee){
			cout << "从" << g.vexs[vv].name << "到" << g.vexs[ee].name << "的路径长度为:" << dist[i] << "\t路径为:";

			d = 0; apath[d] = g.vexs[i].name;//源点到i的最短路径上的终点为i,终点i存放在apath[0]
			k = path[i];//源点到i的最短路径上,顶点i的前一个顶点为k
			if (k == -1)
				//没有路径的情况
				cout << "无路径\n";
			else//存在源点到i的最短路径时输出该路径
			{
				while (k != vv)//当k值等于源点编号时,循环结束
				{
					d++; apath[d] = g.vexs[k].name;//将源点v到i的最短路径上所经过的顶点编号,逆序存放在apath[d] 
					k = path[k];
				}
				d++; apath[d] = g.vexs[vv].name;//存储源点v到i的最短路径上的起点v

				cout << apath[d];//先输出起点
				for (j = d - 1; j >= 0; j--)//再输出其他顶点
				{
					cout <<"->"<< apath[j];
				}

			}
		}
	}
}
//求任一两地点之间的最短路径和长度
void shortestPath(Graph g, int v,int e)

{
	int vv = v;
	int ee = e;
	int dist[MAXVEX], path[MAXVEX];
	int F[MAXVEX];
	int mindis, i, j, u;
	for (i = 0; i<g.numVertexes; i++)
	{
		dist[i] = g.arc[v][i];	 //距离初始化
		F[i] = 0;			 //F[]置空
		if (g.arc[v][i]<INF) //路径初始化 等价于如果有边
			path[i] = v;		 //顶点v到i有边时
		else
			path[i] = -1;		 //顶点v到i没边时
	}
	F[v] = 1;	 		 //源点v放入S中
	for (i = 0; i<g.numVertexes; i++)	 	//循环n次
	{
		mindis = INF;
		for (j = 0; j<g.numVertexes; j++)
			if (F[j] == 0 && dist[j]<mindis)
			{
				u = j;
				mindis = dist[j];
			}
		//优化思路:如果F[ee]=1,那么程序就可以结束了
		F[u] = 1;				//顶点u加入S中
		for (j = 0; j<g.numVertexes; j++)		//修改不在s中的顶点的距离
			if (F[j] == 0)
				if (dist[u] + g.arc[u][j]<dist[j])
				{
					dist[j] = dist[u] + g.arc[u][j];
					path[j] = u;
				}
	}
	Dispath(dist, path, vv,ee,g);		//输出最短路径
	cout << endl;
}

5 . menum函数:
这个函数就是为了显示校园地图系统的菜单,也就是将一堆输出语句封装成一个函数,给出以下代码段

//菜单栏
void menum() {
	cout << setw(75) << "欢迎来到广东理工学院智能地图查询系统" << endl;
	cout << "您可进行以下操作,需要哪种操作请输入相对应的数字!" << endl;
	cout << "1、查询广东理工学院所有地点信息" << endl;
	cout << "2、查询广东理工学院任一地点的基本信息" << endl;
	cout << "3、查询广东理工学院任两个地点间的最短路径和长度" << endl;
	cout << "4、退出系统" << endl;
}

6 . function函数:
这个函数将整个校园地图系统所有的操作都封装在这个函数里面,使主函数变得更加简洁,基本原理就是用while循环嵌套switch语句实现一个智能校园地图系统,给出以下代码段

int function(){
	Graph g;
	Graph *ptr = &g;
	createGraph(ptr); //邻接矩阵创建图
	int  v, e;
	char chose;
	string str, start, end;
	while (true) {
		menum();
		cout << endl;
		cout << "请输入相应的数字编号" << endl;
		cin >> chose;
		switch (chose) {
		case '1':
			printGraph(g);
			break;
		case '2':
			cout << "请输入待查地点的名称:";
			cin >> str;
			if (locates(ptr, str) == -1) {
				cout << "查询地点发生错误,请重新操作!!!!" << endl;
				break;
			}
			else {
				find(g, str);
			}
			break;
		case '3':
			cout << "请输入任意两地的名称,名称之间用空格隔开,以回车结束:" << endl;
			cin >> start >> end;
			v = locates(ptr, start);
			e= locates(ptr, end);
			if (v == -1 || e == -1) {
				cout << "查询地点发生错误,请重新操作!!!!" << endl;
				break;
			}
			else {
				shortestPath(g, v, e);
			}
			cout << endl;
			break;
		case '4':
			cout << "感谢您的光临!" << endl;
			return 0;
		default:
			cout << "操作数输入错误,请重新操作!" << endl;
			break;
		}
		cin.ignore(100, '\n');
	}
}

7 . 主函数main:
因为之前做了许多封装,所以主函数的内容十分简洁

int main() {
	function();
	system("pause");  //这个是针对于visual studio编译环境加上去的,不然控制台界面会一闪而过
	return 0;
}

8 . 源程序

#include 
#include     //因为使用了string字符串类型,需要包含头文件string
#include    //因为使用了setw设置输出流宽度的语句,需要包含头文件iomanip
using namespace std;

#define MAXVEX  15          //顶点数,应由用户定义
#define NUMBER  22          //边数,应由用户定义
#define INF 9999999    //定义一个无穷大数

typedef string VertexName;             //顶点名称应由用户定义
typedef char replace;                  //顶点代号应由用户定义
typedef string info;                   //顶点简介应由用户定义
typedef struct {
	VertexName name; //地点名称
	replace ch; //地点代号
	info str; //地点简介
}Vexs;

typedef int EdgeType;                  //边上的权值类型应由用户定义
typedef struct {
	Vexs vexs[MAXVEX];            //顶点表(以中文信息存储)结构体数组
	EdgeType   arc[MAXVEX][MAXVEX];         //邻接矩阵,可看作边
	int numVertexes, numEdges;      //图中当前的顶点数和边数
}Graph;



//事先把地图信息存储起来
//地点名称
VertexName vexname[15] = {
	"振华楼",
	"学院练车场",
	"美食广场",
	"春晖苑",
	"学校游泳池",
	"东区足球场",
	"翔中堂",
	"健身房",
	"电工房",
	"科技广场",
	"生活服务中心",
	"桃李苑",
	"图书馆",
	"田径场",
	"体育馆"
}; 
//地点代号
replace vexch[15] = {
	'Z','L','M','H','Y','Q','X','J','D','K','S','T','G','C','P'
};
//地点简介
info vexstr[15] = {
	"振华楼是信息技术学院学员的主要活动场所,内置教室数百间,室内硬件配置齐全,当为模范教学楼!",
	"学院练车场是与学校共同合作的一个校外商家,致力于为学生提供物美价廉的考驾照之旅,练车场位于校内,十分方便学生全天候练车!",
	"美食广场位于广东理工学院东区区域,与春晖苑相邻,其中店家琳琅满目,数不胜数,风格深受学生欢迎,是校园的一处好栖息地!",
	"春晖苑也被称之为东区食堂,与美食广场共同提供了学生们的日常餐饮生活,相对比与美食广场而言,春晖苑的装饰更为古风一些!",
	"学校游泳池位于学校的东区区域,泳池是室内的,所以在任何时间段都十分适合学生下水,当然夏日泳池的plmm才是不可多得的一道风景线!",
	"东区足球场,与学校游泳池相邻,有着良好的草地质感,给予运动员最舒适最安全的比赛场地,见证了无数广理足星的诞生!",
	"翔中堂也被称之为礼堂,翔中堂主要作为大型活动的场地,内部有覆盖全场的空调,观众席十分豪华,并且座椅多达两层楼之多!",
	"健身房位于礼堂的对角处,它是学生健身爱好者的天堂,内部硬件设施完善,安全设施齐全,如果店家可以免费让学生体验,再好不过了!",
	"电工房位于校园的中间部分,当学生们宿舍发生电路问题时,都可以求助电工房来进行修理,当然电工一般是十分忙绿的,毕竟只有一所电工房!",
	"科技广场是我校的重要场所,其广场里的科技之星雕塑更是我校的标示性建筑,寓意着师生不断创新,追求真理的科技之心!",
	"生活服务中心也被学生们称之为西区超市,因为其建筑和超市并无两样,除此之外,最大的特色就是网红街,试想,一个宁静的夜晚,在满是星星灯下的你和好朋友们畅聊着星辰大海,是多么惬意的事情!",
	"桃李苑位于西区区域,也称之为西区饭堂,不过其内部的装饰还是店家的款式,都比春晖苑要好一些!",
	"图书馆位于我校正门的正前方,正所谓一进门就得以闻其书香气,图书馆是追求知识的天堂,里面有着一群志同道合,向往远方的青年少女,一起为自己的未来而去奋斗!",
	"田径场是我校举行校运会的主要场地,有着400米的塑胶跑道,为西区的学生们提供了不可多得的课后锻炼场所!",
	"体育馆与田径场相邻,这是我校唯一的室内运动馆,内有一个篮球场和四个羽毛球场,如果能再多一些室内场地,那将十分深受学生们的欢迎!",
};


//初始化地点名称1
string loadingOne[22] = {
	"振华楼",
	"学院练车场",
	"美食广场",
	"振华楼",
	"振华楼",
	"振华楼",
	"学校游泳池",
	"东区足球场",
	"学校游泳池",
	"东区足球场",
	"翔中堂",
	"翔中堂",
	"健身房",
	"电工房",
	"电工房",
	"科技广场",
	"科技广场",
	"田径场",
	"图书馆",
	"图书馆",
	"桃李苑",
	"生活服务中心"
}; 
//初始化地点名称2
string loadingTwo[22] = {
	"学院练车场",
	"美食广场",
	"春晖苑",
	"美食广场",
	"春晖苑",
	"学校游泳池",
	"东区足球场",
	"春晖苑",
	"翔中堂",
	"健身房",
	"健身房",
	"电工房",
	"电工房",
	"科技广场",
	"生活服务中心",
	"图书馆",
	"田径场",
	"体育馆",
	"田径场",
	"桃李苑",
	"生活服务中心",
	"图书馆"
};
//初始化地点路程
int loadingNum[22] = {
	30,10,5,20,
	25,50,5,50,
	30,35,7,20,
	15,50,100,35,
	10,5,10,20,
	25,40
};

//定位
int locates(Graph *g, string ch) {
	int i = 0;
	for (i = 0; i < g->numVertexes; i++) {
		if (g->vexs[i].name == ch) {
			break;
		}
	}
	if (i >= g->numVertexes) {
		return -1;
	}
	return i;
}

//建立一个无向网图的邻接矩阵表示
void createGraph(Graph *g) {

	int i, j, k, w;
	//初始化顶点数和边数
	g->numVertexes = MAXVEX; //15个顶点
	 g->numEdges= NUMBER;    //22条边
	
	//初始化广东理工学院地点名称、代号、简介
	 for (i = 0; i < g->numVertexes; i++) {
		 g->vexs[i].name = vexname[i];
		 g->vexs[i].ch = vexch[i];
		 g->vexs[i].str = vexstr[i];
	 }

	 //邻接矩阵初始化为每个单元为INF
	for (i = 0; i < g->numVertexes; i++) {
		for (j = 0; j < g->numVertexes; j++) {
			g->arc[i][j] = INF;
		}
	}
	cout << endl;

	//初始化地图的各地点权值矩阵
	for (k = 0; k < g->numEdges; k++) {
		string p, q;
		p = loadingOne[k];
		q = loadingTwo[k];
		w=loadingNum[k];
		int m = -1;
		int n = -1;
		m = locates(g, p);//若m等于-1,说明在矩阵中并没有定位到
		n = locates(g, q);
		if (m == -1 || n == -1) {
			cout << "there is no vertex." << endl; //显示输入有错信息
			return;
		}
		g->arc[m][n] = w;
		g->arc[n][m] = w; //因为是无向图,矩阵对称
	}
}

//打印所有地点的基本信息
void printGraph(Graph g) {
	int i;
	for (i = 0; i < g.numVertexes;i++){
		cout << "地点名称:" << g.vexs[i].name << endl;
		cout << "代号:"<< g.vexs[i].ch << endl;
		cout << "简介:" << g.vexs[i].str << endl;
		cout << endl;
	}
}

//打印任一地点的基本信息
void find(Graph g,string ch){
	int i;
	for (i = 0; i < g.numVertexes; i++) {
		if (ch == g.vexs[i].name) {
			cout << "地点名称:" << g.vexs[i].name << endl;
			cout << "代号:" << g.vexs[i].ch << endl;
			cout << "简介:" << g.vexs[i].str << endl;
			cout << endl;
		}
	}
}

//菜单栏
void menum() {
	cout << setw(75) << "欢迎来到广东理工学院智能地图查询系统" << endl;
	cout << "您可进行以下操作,需要哪种操作请输入相对应的数字!" << endl;
	cout << "1、查询广东理工学院所有地点信息" << endl;
	cout << "2、查询广东理工学院任一地点的基本信息" << endl;
	cout << "3、查询广东理工学院任两个地点间的最短路径和长度" << endl;
	cout << "4、退出系统" << endl;
}

//输出任一两点间的最短路径与长度
void Dispath(int dist[], int path[], int vv,int ee,Graph g)//输出从源点v出发的所有最短路径
{
	int i, j, k;
	int  d;//存放一条最短路径(逆向)及其顶点个数
	string apath[MAXVEX];
	for (i = 0; i < g.numVertexes; i++)//n为顶点个数,循环n次,输出源点到其余各个顶点的最短路径和路径长度
	{
		if (i == ee){
			cout << "从" << g.vexs[vv].name << "到" << g.vexs[ee].name << "的路径长度为:" << dist[i] << "\t路径为:";

			d = 0; apath[d] = g.vexs[i].name;//源点到i的最短路径上的终点为i,终点i存放在apath[0]
			k = path[i];//源点到i的最短路径上,顶点i的前一个顶点为k
			if (k == -1)
				//没有路径的情况
				cout << "无路径\n";
			else//存在源点到i的最短路径时输出该路径
			{
				while (k != vv)//当k值等于源点编号时,循环结束
				{
					d++; apath[d] = g.vexs[k].name;//将源点v到i的最短路径上所经过的顶点编号,逆序存放在apath[d] 
					k = path[k];
				}
				d++; apath[d] = g.vexs[vv].name;//存储源点v到i的最短路径上的起点v

				cout << apath[d];//先输出起点
				for (j = d - 1; j >= 0; j--)//再输出其他顶点
				{
					cout <<"->"<< apath[j];
				}

			}
		}
	}
}
//求任一两地点之间的最短路径和长度
void shortestPath(Graph g, int v,int e)

{
	int vv = v;
	int ee = e;
	int dist[MAXVEX], path[MAXVEX];
	int F[MAXVEX];
	int mindis, i, j, u;
	for (i = 0; i<g.numVertexes; i++)
	{
		dist[i] = g.arc[v][i];	 //距离初始化
		F[i] = 0;			 //F[]置空
		if (g.arc[v][i]<INF) //路径初始化 等价于如果有边
			path[i] = v;		 //顶点v到i有边时
		else
			path[i] = -1;		 //顶点v到i没边时
	}
	F[v] = 1;	 		 //源点v放入S中
	for (i = 0; i<g.numVertexes; i++)	 	//循环n次
	{
		mindis = INF;
		for (j = 0; j<g.numVertexes; j++)
			if (F[j] == 0 && dist[j]<mindis)
			{
				u = j;
				mindis = dist[j];
			}
		//优化思路:如果F[ee]=1,那么程序就可以结束了
		F[u] = 1;				//顶点u加入S中
		for (j = 0; j<g.numVertexes; j++)		//修改不在s中的顶点的距离
			if (F[j] == 0)
				if (dist[u] + g.arc[u][j]<dist[j])
				{
					dist[j] = dist[u] + g.arc[u][j];
					path[j] = u;
				}
	}
	Dispath(dist, path, vv,ee,g);		//输出最短路径
	cout << endl;
}

//功能实现
int function(){
	Graph g;
	Graph *ptr = &g;
	createGraph(ptr); //邻接矩阵创建图
	int  v, e;
	char chose;
	string str, start, end;
	while (true) {
		menum();
		cout << endl;
		cout << "请输入相应的数字编号" << endl;
		cin >> chose;
		switch (chose) {
		case '1':
			printGraph(g);
			break;
		case '2':
			cout << "请输入待查地点的名称:";
			cin >> str;
			if (locates(ptr, str) == -1) {
				cout << "查询地点发生错误,请重新操作!!!!" << endl;
				break;
			}
			else {
				find(g, str);
			}
			break;
		case '3':
			cout << "请输入任意两地的名称,名称之间用空格隔开,以回车结束:" << endl;
			cin >> start >> end;
			v = locates(ptr, start);
			e= locates(ptr, end);
			if (v == -1 || e == -1) {
				cout << "查询地点发生错误,请重新操作!!!!" << endl;
				break;
			}
			else {
				shortestPath(g, v, e);
			}
			cout << endl;
			break;
		case '4':
			cout << "感谢您的光临!" << endl;
			return 0;
		default:
			cout << "操作数输入错误,请重新操作!" << endl;
			break;
		}
		cin.ignore(100, '\n');
	}
}
int main(int argc, char **argv) {
	function();
	system("pause");
	return 0;
}

结束语:

在设计过程中其实还是存在一些小bug,由于时间原因,我会在后续的文章中同大家一起分享;对于两点间最短路径的算法,大家可以尝试着去优化它,并可以在评论里和大家一起分享,技术永无止境,这是咕咕的第一篇文章,虽然内容比较基础,但是我会将写技术博客作为自己的总结方式,请大家多多支持呀,一起努力,一起上进鸭!

你可能感兴趣的:(笔记,c++,程序设计)