一、任务内容
本次课程设计要求协助中国大学生计算机设计大赛江苏省组委会,设计一款赛事管理系统,实现赛务相关的数据管理及信息服务,该系统能够为省级赛事管理解决以下问题:
(1)赛事信息管理:从team.txt中读取参赛队伍的基本信息,设计合适的数据结构存储,能实现对参赛队伍的增加、修改和浏览。为参赛队伍分配一个分数为60~100之间的初赛成绩,并能实现参赛队伍的成绩查询(实现基于二叉排序树的查找)。设计合适的输入输出,根据提示输入参赛队编号,查询队伍的初赛成绩,若查找成功,输出该赛事类别对应的基本信息(参赛作品名称、参赛学校、赛事类别、参赛者和初赛成绩信息)。另外,输出全部参赛队的平均查找长度ASL。
(2)决赛现场模拟:首先进行决赛分组,生成决赛秩序册,供参赛队查询。根据赛事类别将参赛队伍分配到17个决赛室(编号为1~17)。秩序册中每个决赛室的进场顺序为初赛成绩降序排列。(排序算法从选择排序、插入排序、希尔排序、归并排序、堆排序中选择一种,并为选择该算法的原因做出说明)然后,模拟决赛秩序。比赛现场会设置大型候赛区,场地中有大屏以时间线动态展示各决赛室中正在决赛的队伍,侯赛的队伍及比赛结束的队伍信息。请编写程序模拟候赛区大屏上动态展示各参赛队候场、比赛中、比赛结束的状态。
(3)决赛地图导览:为参赛者提供决赛主办地的各种路径导航的查询服务,以我校长山校区提供比赛场地为例,为参赛者提供不少于12个目标地的导航。为参赛者提供校园地图中任意目标地(建筑物)相关信息的查询;提供图中任意目标地(建筑物)的问路查询。
二、任务分析
1.参赛队伍信息管理系统可以通过数据库或者文件存储的方式来存储参赛队伍的信息。对于增加、浏览、修改参赛队伍的信息,可以提供相应的操作界面,让用户输入信息进行相应的操作。增加参赛队伍的信息时,用户需要输入参赛队编号、参赛作品名称、参赛学校、赛事类别、参赛者和指导老师等信息,并将其保存到数据库或文件中。浏览参赛队伍的信息时,用户需要输入参赛队编号或其他信息来确定要浏览的参赛队伍,在确认后进行浏览操作。修改参赛队伍的信息时,用户需要输入要修改的参赛队伍的信息,以及需要修改的内容,在确认后进行修改操作
2.数据存储结构选择:由于参赛队伍信息需要进行查找操作,可以选择使用二叉排序树(也称为二叉搜索树)来存储参赛队伍的基本信息。二叉排序树的特点是左子树上的所有节点都小于根节点,右子树上的所有节点都大于根节点,这样可以方便进行查找操作。
3.查询决赛秩序册时,用户需要输入参赛学校名称,程序会按照赛事类别进行排序,输出该学校参赛的所有团队的基本信息。每个决赛室进场顺序的排序算法可以选择选择排序、插入排序、希尔排序、归并排序、堆排序中的任意一种算法。选择排序算法需要进行多次遍历,时间复杂度较大,不适用于数据量过大的情况。插入排序算法比较适合小规模的数据集,但是在大规模数据集下,时间复杂度会很高。希尔排序算法可以提高插入排序的效率,但是实现较为复杂。归并排序算法的时间复杂度为O(nlogn),适合处理大规模数据集,但是需要占用额外的内存空间。堆排序算法的时间复杂度也为O(nlogn),但是需要进行多次数据交换,在数据规模较小的情况下效率较慢。因此,在满足要求的前提下,可以根据实际情况选择排序算法。
4.模拟决赛秩序系统可以设置一个队列来存储参赛队伍的编号,按照赛事类别将参赛队伍分配进17个决赛室。每个决赛室设置一个标志位,用于表示当前是否有参赛队伍正在比赛中。当一个参赛队伍完成比赛后,再从队列中获取下一个参赛队伍的编号,依此类推,直到所有的参赛队伍完成比赛。可以通过时钟等方式模拟时间流逝和叫号顺序,并在屏幕上展示各个决赛室的参赛队伍进场情况。
5.决赛地图导览:使用了图的数据结构和Floyd算法来计算最短路径。其中,建筑物用Building结构体表示,每个建筑物有一个名称和编号。边用Edge结构体表示,每个边有目的地建筑物的索引和路径长度。Campus_navigation类实现了导航功能,通过添加建筑物和路径来构建整个校园的邻接矩阵。在导航函数中,先判断输入的起始地和目的地是否存在,然后使用Floyd算法计算最短路径和下一个节点,并输出最短路径长度以及路径导航。print函数是递归打印Floyd算法计算的最短路径,从源节点到目的节点,输出路径上经过的建筑物名称。
三、部分核心代码
1.基本结构体定义
结构体Team用于储存参赛队编号,参赛作品名称,参赛学校,赛事类别,参赛者,指导老师相关信息。
struct Team{
string id; //编号
string OpusName;//作品名
string School;//学校
string EventCategory;//类别
string Player;//参赛者
string Teacher;//老师
};
2.添加队伍信息
函数addTeam,将输入的信息插入到新结点中。
void addTeam() {
Team s;
cout << "请输入参赛团队编号: ";
cin >> s.id;
cout << "请输入参赛团队作品名称: ";
cin >> s.OpusName;
cout << "请输入参赛团队学校: ";
cin >> s.School;
cout << "请输入参赛类别: ";
cin >> s.EventCategory;
cout << "请输入参赛队员: ";
cin >> s.Player;
cout << "请输入指导老师: ";
cin >> s.Teacher;
insert(s); //将输入的信息插入到新结点中
}
3.修改队伍信息
modifyTeam函数,输入要修改的队伍编号,重新输入该队伍信息。
void modifyTeam(string id) {
deleteNode(root,id); // 删除结点信息
Team s;
cout << "请输入参赛团队编号: ";
cin >> s.id;
cout << "请输入参赛团队作品名称: ";
cin >> s.OpusName;
cout << "请输入参赛团队学校: ";
cin >> s.School;
cout << "请输入参赛类别: ";
cin >> s.EventCategory;
cout << "请输入参赛队员: ";
cin >> s.Player;
cout << "请输入指导老师: ";
cin >> s.Teacher;
insert(s); // 插入到二叉树中
}
4.二叉树查找
创建二叉树的类,
Team *root = NULL;
int searchCount = 0;
int searchTotal = 0;
结点结构体,包括信息,左右字子树
struct Node {
Team data;
Node* left;
Node* right;
};
createNode函数,创建新结点
Node* createNode(Team data) {
Node* newNode = new Node();
newNode->data = data;
newNode->left = NULL;
newNode->right = NULL;
return newNode;
}
insertNode函数,将新结点插入到二叉树中。(平衡二叉树,方便按编号大有序输出队伍信息)
insert函数,调用insertNode函数,来插入队伍信息,读取文件和添加修改队伍信息也会用到。
Node* insertNode(Node* node, Team data) {
if (node == NULL) { // 若结点为空,将新结点作为根结点
size++; //结点数加1
return createNode(data);
}
if (data.id < node->data.id) { // 新结点学号小于当前结点,插入到左子树
node->left = insertNode(node->left, data);
}
else if (data.id > node->data.id) { // 新结点学号小于当前结点,插入到左子树
node->right = insertNode(node->right, data);
}
return node;
}
printTeam函数,输出队伍信息。
void printTeam(Team s) {
cout << "参赛团队编号: " << s.id << endl;
cout << "参赛作品名称: " << s.OpusName << endl;
cout << "参赛团队学校: " << s.School << endl;
cout << "参赛类型: " << s.EventCategory << endl;
cout << "参赛队员: " << s.Player << endl;
cout << "指导老师: " << s.Teacher << endl;
}
searchNode函数,在二叉树中所有参数中的id,并记录查找次数。
search函数,调用searchNode函数来实现对ID的查找。
//在二叉树中搜索该学号
Node* searchNode(Node* node, string id, int& count) {
if (node == NULL) { // 若结点为空或没有找到,返回NULL
return NULL;
}
count++; // 查找次数加一
if (id == node->data.id) { // 如果找到匹配的结点,返回该结点指针
return node;
}
if (id < node->data.id) { // 若查找学号小于该结点学号,在左子树中查找
return searchNode(node->left, id, count);
}
else { // 若查找学号大于该结点学号,在右子树中查找
return searchNode(node->right, id, count);
}
}
Node* search(string id) {
int count = 0; // 记录查找次数
Node* node = searchNode(root, id, count);
totalSearch += count; // 查找次数累加
return node; // 返回查找结点
}
getASL函数,计算平均查找长度。
// 平均查找长度ASL
double getASL() {
if (size == 0) { // 若树为空,返回0
return 0;
}
return (double)totalSearch / size;
}
printSameSchool函数,找到该学校所有参赛队伍的信息。
void printSameSchool(Node* root,string School) {
if (root == NULL) return; // 结点为空返回
printSameSchool(root->left,School); // 遍历左子树
if (root->data.School == School) { // 当前结点和输入学校相同,输出该结点的信息
printTeam(root->data);
}
printSameSchool(root->right,School); // 遍历右子树
}