数据结构课程设计任务书
课程名称 | 数据结构与算法课程设计 | ||
设计题目 | 校园导航问题 | ||
指导教师 | 时间 | 2023.12.25-2023.12.30 | |
一、教学要求 1. 掌握数据结构与算法的设计方法,具备初步的独立分析和设计能力 2. 初步掌握软件开发过程的问题分析、系统设计、程序编码、测试等基本方法和技能 3. 提高综合运用所学的理论知识和方法独立分析和解决问题的能力 4. 训练用系统的观点和软件开发一般规范进行软件开发,培养软件工作者所应具备的科学的工作方法和作风 二、设计资料及参数 每个学生在教师提供的课程设计题目中任意选择一题,独立完成,题目选定后不可更换。 校园导航问题 以数组表示法表示校园平面图,在此基础上实现求校园任意两点的最短路径。 要求设计类(或类模板)来描述图,包含必要的构造函数和析构函数,以及其他能够完成如下功能的成员函数:
并设计主函数测试该类(或类模板)。 三、设计要求及成果 1. 分析课程设计题目的要求 四、进度安排 资料查阅与讨论(1天) 系统分析(1天) 系统的开发与测试(2天) 编写课程设计说明书和验收(1天) 五、评分标准 1. 根据平时上机考勤、表现和进度,教师将每天点名和检查 2. 根据课程设计完成情况,必须有可运行的软件。 4. 根据答辩的情况,应能够以清晰的思路和准确、简练的语言叙述自己的设计和回答教师的提问 六、建议参考资料 1.《数据结构 (C语言版)》严蔚敏、吴伟民 主编 清华大学出版社 2013 2.《数据结构课程设计案例精编(用C/C++描述)》,李建学 等 编著,清华大学出版社 2010 3.《数据结构:用面向对象方法与C++语言描述》,殷人昆 主编, 清华大学出版社 2012 七、设计步骤 1. 问题定义与需求分析:根据设计题目的要求,充分地分析和理解问题,确定功能需求和限制条件。 2. 数据结构设计:对问题描述中涉及的操作对象定义相应的数据类型和各抽象数据类型。 3. 概要设计:按照以数据结构为中心的原则划分模块,设计软件层次结构和模块间的调用关系,定义主程序,画出模块之间的调用关系图。在这个过程中,要综合考虑系统功能,使得系统结构清晰、合理、简单和易于调试。 4. 详细设计:定义数据存储结构,各个主要模块的算法定义。详细设计的结果是对数据结构和基本操作作出进一步的求精,写出数据存储结构的类型定义,用伪码写出函数的算法。 5. 程序编码:把详细设计的结果进一步求精为程序设计语言程序。 6. 程序调试与测试 7. 设计结果分析:程序运行结果分析,算法的时间、空间复杂性分析。 8. 编写课程设计报告。
上述数据结构设计、概要设计和详细设计为此课程设计的要点。
解决实际问题的关键仅仅依赖计算机语言是不够的,还必须掌握好数据结构的相关知识。首先要从该具体问题抽象出一个恰当的数学模型,然后设计出解决此类数学模型的算法,再编写相应的程序并进行调试、测试,运行程序并最后得到答案。 十、方案比较内容 每一个题目的实现都可以选择不同的方案,因为每个数据结构可以有不同的存储结构,存储方式不同实现的算法也不同,因此可以根据不同的方案来比较和分析算法的时间和空间效率。 |
第一章 需求分析
现在科技十分发达,计算机在生活中扮演了越来越重要的作用,在现实生活中,大家在一些生活方面仍具有一些问题,诸如:对于便利乘车,系统管理等,在大学生活中,尤其是初入校园的时候,大家都会很迷茫,用计算机实现校园导航就可以很好的解决这个问题,同时也可以让我们的校园生活更加的便利。
以数组表示法表示校园平面图,在此基础上实现求校园任意两点的最短路径。
输入的数据,初始化顶点数目、边的数目、顶点信息(编号、名称、描述)初始化边的值(输入数据的顺序为起点,终点,路径长度)
(仅作为测试,与显示可能略有差距,最大顶点个数NUM为10)
- 数据组1:
顶点数目与边的数目10 14
顶点信息
0 逸夫楼 学术类社团聚集地、平时上课、开会教室
1 体育馆 体育课上课、考试场所
2 明德楼 有部分与逸夫楼相连、上课场所
3 鹿苑 标志性建筑,目前正在改装为停车场
4 工程训练中心 具有开放性机房,是创业基础课等的教学地点
5 秋实楼 具有多个实验室与机房,是教学研究地点
6 文馨书院 学生口中的二教,是教学上课地点
7 活动中心 圆形开放式广场、是大型活动的举办地点
8 春晖学堂 学生口中的主教,是教学上课地点
9 腾飞楼 具有多个专业老师的办公室
初始化边的值
0 1 2
0 2 1
0 3 2
1 4 10
2 4 9
2 5 2
3 2 3
3 9 19
4 7 13
5 6 5
6 7 11
6 8 10
7 8 3
8 9 2
- 数据组2
顶点数目与边的数目5 4
顶点信息
0 三号楼 女生宿舍,住宿地点
1 和园 餐厅,分为左右东和园、西和园
2 雅园 只有一层餐厅,但内有小餐厅
3 煦园 餐厅有两层,附近有教职工食堂
4 颐园 学校内最大的食堂,有三层,有电梯
初始化边的值
0 1 2
0 3 4
1 2 5
2 3 2
Visual Studio 2022
资料查阅与讨论(1天)
系统分析(1天)
系统的开发与测试(2天)
编写课程设计说明书和验收(1天)
typedef struct ArcCell {
int adj;//图中两个地点间的距离
}ArcCell;//边
typedef struct VertexType {
int number;//定义地点的编号
const char* sight;//定义地点的名称
const char* description;//定义地点的描述
}VertexType;//定义地点
typedef struct MGraph {
VertexType vex[NUM];//定义地点
ArcCell arcs[NUM][NUM];//定义地点间的关系
int vexnum;//定义地点数
int arcnum;//定义边的数目
}MGraph;//图
表2.1:函数列表
函数名 | 函数格式 //即函数首部 | 函数功能 |
Creat | void Creat(int v, int a) | 创建图,并输入图的信息 |
pic | void pic() | 打印图邻接矩阵 |
narrate | void narrate() | 打印地点编号和名称 |
Menu | char Menu() | 打印主菜单 |
SearchMenu | char SearchMenu() | 打印查找的目录 |
Search | void Search() | 实现查找的功能 |
Shortest | void Shortest(int num) | 实现找到最短路径的功能 |
output | void output(int sight1, int sight2) | 输出最短路径 |
exist | void exist(int start, int end) | 查询最短路径是否存在 |
输入图的信息运行截图:
输出图的运行截图:
校园导航程序主界面:
查找校园的最短路径运行截图
查询校园建筑信息运行截图
判断是否存在最短路径运行截图
数据组1
数据组2
定义了一个创建图的函数Creat()来输入顶点的编号number、名称sight、描述description,同时输入边的信息起点v1终点v2长度a
利用两个for循环来遍历G.vexnum(顶点数),从而遍历所有的G.arcs[i][j].adj,令G.arcs[i][j].adj==无穷大时输出∞,否则输出边的长度。
从键盘中获取两个地点编号sight1和sight2,当距离D[a]等于无穷大时,输出无最短路径,当最短路径存在且终点不等于起点,输出最短路径,另d等于起点,在b等于终点时查找最短距离,完成一轮查找后,令d=b,也就是第一轮的终点作为起点,继续进行查找,知道结束
利用判断距离实现判断是否存在最短路径Start是起点,end是终点
通过判断是否相等实现简单的查找
利用Dijkstra算法查找最短路径,定义一个求最短路径的函数shortest(int num){},输入的num是起始点,定义一个final[]存放已经求得的路径结果,final[v]为真,表示已经求得的v0到v的最短路径,即v顶点已被访问,其中D[v]是num到v的距离,P[v][w]是num到v的最短距离经过了w。
首先用两个for循环,使D[v]表示可以直接到达的顶点的距离,并将所有路径设置为空,只有将D[w]小于无穷的时候路径才存在,同时设置自身和自身的路径也存在。
设置D[num]=0; final[num]=1;进行for循环,在嵌套的第一个for循环中找到离起点距离最小的顶点,找到后将final[v]设为1标记为已访问,进行嵌套的第二个for循环,通过比较当前最小距离加上从当前最小顶点到其他顶点的距离,与已经求得的最短路径进行比较,如果找到了更短的路径,则更新距离数组D和路径记录数组P。
在设计程序的过程中,一共设计了两组不同的数据来测试程序
概括不同的结果
以下是第二组的测试信息
解决办法:下载一个stdafx.h文件加入源文件
同时在属性——C/C++——预编译头中改为创建
- Printf打印失效问题
解决办法:使用两个getchar()方法,第一个用来清空输入缓冲区的字符(清除回车符),第二个用来实现按任意键返回的功能
- 未能解析 0x234BBD0D、0x00000000、0xC0100040、0x000000C2
解决办法:检测是否出现传值不正确,越界,野指针等问题,我出现的是传值不正确,scanf函数中的%s,%d选择不当。
- scanf("%s %s", G.vex[i].sight, G.vex[i].description);语句报错,无法使用
解决办法:G.vex[i].sight = (char*)malloc(sizeof(char) * 20);
- vex[i].description = (char*)malloc(sizeof(char) * 50);
根据实际情况开辟空间
课程设计到这里就落下帷幕了,在进行校园导航这个课程设计的工程中,总共历时了一个星期,花了一天的时间了解选择的题目,最终决定写校园导航这个项目,选择这个项目的原因有一点是因为它很贴近日常的生活,另一点就是想要补全我在图方向上的欠缺,在复习的过程中我就发现了这个问题,不过时间比较急,所以有所疏漏,这个校园导航程序是我根据一些B站视频、教材书等材料来进行设计的,可以实现图的输入输出,可以查询最短路径,可以按编号地点名等查询整体信息,总体来说,比起一些正常使用的程序,我感觉还是有很多欠缺,希望以后有机会的话可以进一步完善。
最开始进行设计的是最短路径的查找,在开头阶段,我并没有设计输入图的功能,而是给定了一个固定的图,来进行测试,通过一些材料来寻找实现的最佳方法,最后是使用了Dijkstra算法来实现这部分的功能,在设计中,因为是第一次接触使用C语言,在一些地方也出现了很多问题,诸如:更新后SDK库不适用,头文件报错,printf和scanf使用不熟练报地址的错误,也有让人又爱又恨的预编译头,在做课程设计的过程中,我发现了这些错误,同时也深刻的意识到了C和C++的区别并不是我想的那样简单,这也让我更加坚定了要学好的决心,在设计好路径查找后,我又陷入了另一个困境,输入输出图,到底怎么样才算是输入输出,在书写输入图的代码时,顶点数目、边的数目、顶点顶点间距离是较为容易完成的部分,但在输入顶点的信息时,确频频报错,最后经过查找,开辟了动态空间才解决,期间还遇到了printf无法输出的问题,疑似是因为循环语句,最后两个getchar()解决。
课程设计的过程可以说是磕磕绊绊,也有bug半天半天解决不完,但是在课设的过程中,我收获了更为宝贵的经历,和对自己更深刻的认识,这些都会使我变得更加优秀,也在这个过程中锻炼了自己的耐心和细心,更有决心和毅力走的更远。
附录:程序代码
#define _CRT_SECURE_NO_WARNINGS 1
#include "stdafx.h"
#include "stdlib.h"
#include "stdio.h"
#include "string.h"
#include <iostream>
#define Max 30000
#define NUM 10//设置最大上限
typedef struct ArcCell {
int adj;//图中两个地点间的距离
}ArcCell;//边
typedef struct VertexType {
int number;//定义地点的编号
const char* sight;//定义地点的名称
const char* description;//定义地点的描述
}VertexType;//定义地点
typedef struct MGraph {
VertexType vex[NUM];//定义地点
ArcCell arcs[NUM][NUM];//定义地点间的关系
int vexnum;//定义地点数
int arcnum;//定义边的数目
}MGraph;//图
//定义一个图的变量
MGraph G;
int P[NUM][NUM];
long int D[NUM];
//创建图
void Creat(int v, int a) {
int i, j;
G.vexnum = v;//地点数
G.arcnum = a;//边数
/*for (i = 0; i < G.vexnum; ++i)
G.vex[i].number = i;*/
printf("请输入顶点信息(编号、名称、描述):\n");
for (i = 0; i < G.vexnum; i++) {
printf("顶点[%d]: ", i);
scanf("%d", &(G.vex[i].number));
// 根据实际情况分配内存
G.vex[i].sight = (char*)malloc(sizeof(char) * 20);
G.vex[i].description = (char*)malloc(sizeof(char) * 50);
scanf("%s %s", G.vex[i].sight, G.vex[i].description);
}
//边长
for (i = 0; i < G.vexnum; ++i)
for (j = 0; j < G.vexnum; ++j)
G.arcs[i][j].adj = Max;
printf("请设置每条边的起点、终点和长度(如:1 2 3 表示起点为1,终点为2,权值为3):\n");
for (i = 0; i < G.arcnum; i++) {
printf("边[%d]: ", i);
int v1, v2, a;
scanf("%d%d%d", &v1, &v2, &a);
G.arcs[v1][v2].adj = a;
G.arcs[v2][v1].adj = a;
}
}
//打印图
void pic() {
//打印图的信息邻接矩阵
printf("无向网的邻接矩阵存储结构为:\n");
for (int i = 0; i < G.vexnum; i++) {
for (int j = 0; j < G.vexnum; j++) {
if (G.arcs[i][j].adj == Max) {
printf("∞ ");
}
else {
printf("%d ", G.arcs[i][j].adj);
}
}
printf("\n");
}
}
//前言(地图)
void narrate() {
printf("\n\t\t\t 欢迎使用校园导航系统 \n");
printf("\t____________________________________________________________________________\n");
for (int i = 0; i < G.vexnum; i++) {
printf("\t(%d)%-15s\t\t\t\n", i, G.vex[i].sight);
}
printf("\t____________________________________________________________________________\n");
}
//主要功能菜单
char Menu() {
char c;
int flag;
do {
flag = 1;
system("cls");
narrate();//主界面
printf("\t\t\t 欢迎来到主菜单,您可以选择下面的功能编号进行输入\n");
printf("\t\t\t 1.查询校园的最短路径\n");
printf("\t\t\t 2.查看校园的建筑信息\n");
printf("\t\t\t 3.查询是否存在最短路径\n");
printf("\t\t\t t.退出\n");
printf("请输入您的选择:");
scanf("%c", &c);
if (c == '1' || c == '2' || c == '3' ||c == 't')
flag = 0;
} while (flag);
return c;
}
//查找(目录)
char SearchMenu() {
char c;
int flag;
do {
flag = 1;
system("cls");
narrate();
printf("\n\n\t\t\t----------------------------------------\n");
printf("\t\t\t| 欢迎使用查找系统 |\n");
printf("\t\t\t| 1.按照建筑编号查询 |\n");
printf("\t\t\t| 2.按照建筑名称查询 |\n ");
printf("\t\t\t| 3.查询所有地点及信息 |\n");
printf("\t\t\t| t.返回 |\n ");
printf("\t\t\t----------------------------------------\n");
printf("\t\t\t\t请输入您的选择:");
scanf("%c", &c);
if (c == '1' || c == '2' || c == '3'||c=='t')
flag = 0;
} while (flag);
return c;
}
//查找(功能)
void Search() {
int num;//地点编号
int i;
char c;
char name[20];
do {
system("cls");
c = SearchMenu();
switch (c) {
case '1':
system("cls");
narrate();
printf("请输入你要查找的地点的编号:");
scanf("%d", &num);
for (i = 0; i < G.vexnum; i++) {
if (num == G.vex[i].number) {
printf("您要查找的信息如下\n");
printf("%s的信息为:%s \n", G.vex[i].sight, G.vex[i].description);
printf("按任意键返回\n");
getchar();
getchar();
break;
}
}
if (i == G.vexnum ) {
printf("查询地点失败\n");
printf("按任意键返回\n");
getchar();
getchar();
}
break;
case '2':
system("cls");
narrate();
printf("请输入您要查找的地点的名称:");
scanf("%19s", name);
for (i = 0; i < G.vexnum; i++) {
if (!strcmp(name, G.vex[i].sight)) {
printf("%s的信息为:%s", G.vex[i].sight, G.vex[i].description);
printf("\n按任意键返回");
getchar();
getchar();
break;
}
}
if (i == G.vexnum ) {
printf("查询地点失败");
printf("按任意键返回");
getchar();
getchar();
}
break;
case '3':
system("cls");
narrate();
printf("\t \n");
printf("\t____________________________________________________________________________\n");
printf("\t\t建筑名称\t\t\t|\t建筑描述\t\n");
printf("\t____________________________________________________________________________\n");
for (i = 0; i < G.vexnum ; i++) {
printf("\t(%d)%-15s\t\t\t|\t%-25s\n", i, G.vex[i].sight, G.vex[i].description);
}
printf("\t____________________________________________________________________________\n");
printf("按任意键返回");
getchar();
getchar();
break;
}
} while (c != 't');
}
//最短路径
void Shortest(int num) {
int v, w, i, t;
int final[NUM];
int min;
for (v = 0; v < G.vexnum ; ++v) {
//final存放已经求得的路径结果,final[v]为真,表示已经求得的v0到v的最短路径
final[v]=0;
D[v] = G.arcs[num][v].adj;
for (w = 0; w < G.vexnum; ++w) {
//P[v][w]行号表示终点,列号表示经过的路径,若为真,则w是从v0到v当前求得的最短路径上的顶点
P[v][w] = 0;//设置空路径
}
if (D[v] < 30000) {
P[v][num] = 1;
P[v][v] = 1;
}
}//初始化操作结束
D[num] = 0;//v0到v0肯定是0
final[num] = 1;//v0到v0路径已知,所以为真
//比较次数 Dijkstra第一行那个比较次数,与顶点与顶点间比较无关
for (i = 1; i < G.vexnum ; ++i) {
min = Max;
//第一层循环,找出最小值
//如果还没求得最短路径
for (w = 0; w < G.vexnum ; w++) {
if(!final[w])
if (D[w] < min) {
v = w;
min = D[w];
}
}
final[v] = 1;
//求第二次
for (w = 0; w < G.vexnum ; w++) {
if (!final[w] && (min + G.arcs[v][w].adj) < D[w]) {
//修改D[w] P[w]
D[w] = min + G.arcs[v][w].adj;
//这个循环将 P[w][t] 的所有元素设置为 1,表示顶点 w 经过路径 t。
for (t = 0; t < G.vexnum ; t++)
P[w][t] = P[v][t];
P[w][w] = 1;
}
}
}
}
//输出结果
void output(int sight1, int sight2) {
int a, b, d;
a = sight2;
if (D[a] == Max) {
printf("对不起,您选择的这段路程不存在最短路径哦");
}
if (D[a] < Max) {
if (a != sight1)
printf("从%d到%d存在最短路径\n", G.vex[sight1].number, G.vex[sight2].number);
printf("最短距离为:%d \n", D[a]);
printf("最短路径是:%s", G.vex[sight1].sight);
d = sight1;
gate:
P[a][sight1] = 0;
for (b = 0; b < G.vexnum; b++) {
if (G.arcs[d][b].adj < 30000 && P[a][b]) {
printf("-->%s", G.vex[b].sight);
P[a][b] = 0;
//进行下次循环
d = b;
goto gate;
}
}
}
}
//是否存在最短路径
void exist(int start, int end) {
int u, w;
w = end;
if (D[w] == Max) {
printf("很抱歉,您查找的地点间没有最短路径\n");
}
else {
printf("您查找的地点间存在最短路径,可以去往首页查询更具体的路径信息哦\n\n\n");
printf("或者您可以向下继续查看\n");
if (w != start)
printf("从%d到%d存在最短路径\n", G.vex[start].number, G.vex[end].number);
printf("最短距离为:%d \n", D[w]);
printf("最短路径是:%s", G.vex[start].sight);
u = start;
gate:
P[w][start] = 0;
for (int b = 0; b < G.vexnum; b++) {
if (G.arcs[u][b].adj < 30000 && P[w][b]) {
printf("-->%s", G.vex[b].sight);
P[w][b] = 0;
//进行下次循环
u = b;
goto gate;
}
}
}
}
int main() {
int v0, v1;
char ck;//ck用作选择
int v, a;
printf("请输入地点数和边的数目(用空格隔开):");
scanf("%d%d", &v, &a);
Creat(v, a);
pic();
printf("可以自己根据邻接矩阵做出现实中的地图哦");
getchar();
getchar();
do {
ck = Menu();
switch (ck) {
case '1':
system("cls");
printf("请输入起始地点:");
scanf("%d",&v0);
printf("请输入终止地点:");
scanf("%d", &v1);//提示信息
Shortest(v0);
output(v0,v1);
printf("按任意键返回");
getchar();
getchar();
break;
case '2':
system("cls");
Search();
printf("请按任意键继续");
getchar();
getchar();
break;
case'3':
system("cls");
printf("请输入起始地点:");
scanf("%d", &v0);
printf("请输入终止地点:");
scanf("%d", &v1);
Shortest(v0);
exist(v0, v1);
printf("按任意键返回");
getchar();
getchar();
break;
case 't':
system("cls");
return 0;
}
} while (ck!='t');
return 0;
}
参考文献
[1].《数据结构 (C语言版)》严蔚敏、吴伟民 主编 清华大学出版社 2013
[2].《数据结构课程设计案例精编(用C/C++描述)》李建学 等 编著,清华大学出版社 2010
[3].《数据结构:用面向对象方法与C++语言描述》殷人昆 主编,清华大学出版社 2012