目录
中国大学生计算机设计大赛省级赛事管理系统
中国大学生计算机设计大赛是我国高校面向本科生的计算机应用设计大赛,大赛旨在激发学生学习计算机知识和技能的兴趣与潜能,提高学生运用信息技术解决实际问题的综合能力。通过大赛这种计算机教学实践形式,可展示师生的教与学成果,最终以赛促学,以赛促教,以赛促创。该赛事在历届学生中影响力较大,参与者众多,请结合2021届省赛参赛的数据,借助数据结构课程所学的相关知识,通过对数据的处理和分析,全面了解赛事组织及管理的体系,以及数据结构设计及数据处理在信息管理系统中应用的重要性。赛事相关数据存储在文本文件和excel文件中,相应的文件信息说明如表1所示。其中,各个文件中不同的数据项之间均使用#分隔
【问题描述】
要求协助中国大学生计算机设计大赛江苏省组委会,设计一款赛事管理系统,实现赛务相关的数据管理及信息服务,该系统能够为省级赛事管理解决以下问题:
(1)从team.txt中读取参赛队伍的基本信息,能够管理各参赛队的基本信息(包含参赛队编号,参赛作品名称,参赛学校,赛事类别,参赛者,指导老师),赛事类别共11项(参见大赛官网jsjds.blcu.edu.cn);包括增加、删除、修改参赛队伍的信息。
(2)实现基于二叉排序树的查找。根据提示输入参赛队编号,若查找成功,输出该赛事类别对应的基本信息(参赛作品名称、参赛学校、赛事类别、参赛者和指导老师信息),同时,输出查找成功时的平均查找长度ASL;否则,输出“查找失败!”。请输出ASL的计算表达式和结果值。
(3)能够提供按参赛学校查询参赛团队,根据提示输入参赛学校名称,若查找成功,输出该学校参赛的所有团队的基本信息,输出的参赛团队需有序输出(按参赛队编号)。(排序算法可从选择排序、插入排序、希尔排序、归并排序、堆排序中任意选择,并为选择算法的原因做出说明。)
(4)为省赛现场设计一个决赛叫号系统。所有参赛队按赛事组织文件中的赛事类别分到9个决赛室,决赛室按顺序叫号,被叫号参赛队进场,比赛结束后,下一参赛队才能进赛场。请模拟决赛叫号系统,演示省赛现场各决赛室的参赛队进场情况。(模拟时,各参赛队进场比赛时间可设为0.5秒)
(5)赛事系统为参赛者提供赛地的校园导游程序。为参赛者提供各种路径导航的查询服务。以我校长山校区提供比赛场地为例,(请为参赛者提供不少于10个目标地的导航。可为参赛者提供校园地图中任意目标地(建筑物)相关信息的查询;提供图中任意目标地(建筑物)的问路查询,即查询任意两个目的地(建筑物)之间的一条最短的简单路径。
1.管理队伍
1.1问题描述
能够管理各参赛队的基本信息(包含参赛队编号,参赛作品名称,参赛学校,赛事类别,参赛者,指导老师),赛事类别共11项(参见大赛官网jsjds.blcu.edu.cn);包括增加、删除、修改参赛队伍的信息。
1.2问题分析
可以使用一个类表示参赛队伍,将该类视为一个实体对象,用于存储和管理参赛队伍的各项信息。
参赛队伍信息的存储:使用一个类来表示参赛队伍的信息。类中的属性(成员变量)可以包含参赛队编号、参赛作品名称、参赛学校、赛事类别、参赛者和指导老师等。通过创建类的对象,可以实例化并存储每个参赛队伍的具体信息。
增加:通过创建新的类对象,提供参赛队伍的信息作为构造函数的参数,将其存储到适当的数据结构(如列表或文件)中。
删除:通过参赛队伍的唯一标识符(如队伍编号)来查找并删除相应的类对象。
修改:通过参赛队伍的唯一标识符来查找相应的类对象,并修改其属性值。
参赛队伍信息的查询:通过参赛队伍的唯一标识符(如队伍编号)或其他条件,从数据结构中查找并获取相应的类对象。可以定义方法或提供适当的接口来根据不同的查询条件进行搜索。
文件的读写:使用IO流来读取和写入参赛队伍信息到文件中。可以使用FileReader和FileWriter或其他相应的IO类来实现文件的读写操作。
当程序启动或退出时,可以将数据从内存中的Team对象保存到文件中,以便实现数据的持久化和持续访问。
1.3设计思路
首先,在Team类中,我定义了表示参赛队伍的基本信息的属性,包括队伍编号(serial_number)、队伍名称(name)、学校名称(school)、赛事类别(kind)、参赛成员(team_member)和指导老师(teacher)。这些属性都有对应的getter和setter方法,以及一个重写的toString()方法用于打印对象的字符串表示。
下面这些方法用来操作参赛队伍的信息。
add(String s)方法用于将参赛队伍的信息添加到文件中。它使用FileWriter将信息写入指定的文件路径中。
look()方法用于读取文件中的参赛队伍信息并打印出来。它使用FileReader读取文件内容,并使用while循环逐个字符地读取文件内容并打印。
delete()方法用于删除文件中指定的参赛队伍信息。它使用Scanner从用户输入中获取要删除的信息,然后使用BufferedReader和BufferedWriter读取和写入文件内容,将不需要删除的信息写入临时文件,最后将临时文件重命名为原始文件。
upData()方法用于更新文件中指定的参赛队伍信息。它使用FileReader和BufferedReader读取原始文件内容,然后使用FileWriter和BufferedWriter写入修改后的内容到临时文件中。在读取文件内容的过程中,使用Scanner从用户输入中获取要匹配的字符串,然后根据匹配结果进行修改。最后,删除原始文件并将临时文件重命名为原始文件。
同时,这些方法都依赖于文件的读写操作。在调用这些方法之前,需要提供正确的文件路径,确保文件存在并且具有读写权限。
1.4代码
package com.it.DS;
public class Team {
public int serial_number;
public String name;
public String school;
public String kind;
public char[] team_member;
public String teacher;
public Team childL=null;//左子节点
public Team childR=null;//右子节点
public Team() {
}
public Team(int serial_number, String name, String school, String kind, char[] team_member, String teacher) {
this.serial_number = serial_number;
this.name = name;
this.school = school;
this.kind = kind;
this.team_member = team_member;
this.teacher = teacher;
}
public int getSerial_number() {
return serial_number;
}
public void setSerial_number(int serial_number) {
this.serial_number = serial_number;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSchool() {
return school;
}
public void setSchool(String school) {
this.school = school;
}
public String getKind() {
return kind;
}
public void setKind(String kind) {
this.kind = kind;
}
public char[] getTeam_member() {
return team_member;
}
public void setTeam_member(char[] team_member) {
this.team_member = team_member;
}
public String getTeacher() {
return teacher;
}
public void setTeacher(String teacher) {
this.teacher = teacher;
}
@Override
public String toString() {
return
serial_number +" "+
name + '\'' +" "+
school + '\'' +" "+
kind + '\'' +" "+
new String(team_member) +" "+
teacher + '\'' ;
}
}
1.5结果展示
2.二叉查找树
2.1问题描述
从team.txt中读取参赛队伍的基本信息,实现基于二叉排序树的查找。根据提示输入参赛队编号,若查找成功,输出该赛事类别对应的基本信息(参赛作品名称、参赛学校、赛事类别、参赛者和指导老师信息),同时,输出查找成功时的平均查找长度ASL;否则,输出“查找失败!”。
2.2问题分析
从team.txt文件中读取参赛队伍的基本信息,并将每个队伍作为一个Team对象插入二叉排序树中。每个节点的键值可以设定为参赛队编号。
实现基于二叉排序树的查找功能。根据提示输入参赛队编号,从二叉排序树中查找对应的节点。如果查找成功,输出该赛事类别的基本信息,并计算平均查找长度(ASL)。否则,输出"查找失败!"。ASL的计算方法为用总搜索长度除去总结点数
2.3设计思路
数据结构:使用BinarySearchTree类来表示二叉排序树,其中每个节点存储一个参赛队伍的信息。Team类表示参赛队伍,包含参赛队编号、参赛作品名称、参赛学校、赛事类别、参赛者和指导老师信息。
插入操作:insert方法用于向二叉排序树中插入参赛队伍。如果二叉排序树为空,则将队伍作为根节点插入。否则,通过递归调用insertRecursively方法,在合适的位置递归地插入队伍。
查找操作:find方法用于根据参赛队编号查找队伍。在二叉排序树中通过递归查找,如果找到匹配的队伍或达到叶子节点,则返回结果。
平均查找长度计算:calculateASL方法用于计算平均查找长度(ASL)。通过调用calculateSearchLength方法来递归计算查找长度,并记录总查找长度和节点数量。最后,根据公式(总查找长度 / 节点数量)计算ASL。
删除操作:deleteTree方法用于删除二叉排序树,将根节点置为空。
2.4代码
package com.it.DS;
public class BinarySearchTree {
public Team root;
public int totalSearchLength;
public int totalNodes;
public BinarySearchTree() {
root = null;
totalSearchLength = 0;
totalNodes = 0;
}
public void insert(Team team) {
if (root == null) {
root = team;
} else {
insertRecursively(root, team);
}
totalNodes++;
}
private void insertRecursively(Team currentNode, Team team) {
if (team.getSerial_number() < currentNode.getSerial_number()) {
if (currentNode.childL == null) {
currentNode.childL = team;
} else {
insertRecursively(currentNode.childL, team);
}
} else if (team.getSerial_number() > currentNode.getSerial_number()) {
if (currentNode.childR == null) {
currentNode.childR = team;
} else {
insertRecursively(currentNode.childR, team);
}
}
}
public Team find(int serial_number) {
return findRecursively(root, serial_number);
}
private Team findRecursively(Team currentNode, int serial_number) {
if (currentNode == null || currentNode.getSerial_number() == serial_number) {
return currentNode;
}
if (serial_number < currentNode.getSerial_number()) {
return findRecursively(currentNode.childL, serial_number);
} else {
return findRecursively(currentNode.childR, serial_number);
}
}
public double calculateASL() {
calculateSearchLength(root, 1);
double asl = (double) totalSearchLength / totalNodes;
System.out.println("ASL 计算表达式为: (" + totalSearchLength + " / " + totalNodes + ") = " + asl);
return asl;
}
private void calculateSearchLength(Team currentNode, int depth) {
if (currentNode == null) {
return;
}
totalSearchLength += depth;
calculateSearchLength(currentNode.childL, depth + 1);
calculateSearchLength(currentNode.childR, depth + 1);
}
public void deleteTree() {
root = null;
}
}
2.5结果展示
3.归并排序及查找
3.1问题描述
能够提供按参赛学校查询参赛团队(或根据赛事类别查询参赛团队),即,根据提示输入参赛学校名称(赛事类别),若查找成功,输出该学校参赛的(该赛事类别的)所有团队的基本信息,输出的参赛团队按赛事类别有序输出。(排序算法可从选择排序、插入排序、希尔排序、归并排序、堆排序中任意选择,并为选择算法的原因做出说明。)
3.2问题分析
可采用集合用来存放所有Team类,并根据集合的方法进行按学校名称、按类型分类
而采用归并排序,是因为该算法具有:
稳定性:归并排序是一种稳定的排序算法,即相等元素的相对位置在排序前后保持不变。这对于某些应用场景非常重要。
时间复杂度:归并排序的时间复杂度为O(nlogn),其中n是待排序序列的长度。它的时间复杂度相对较低,适用于大规模数据的排序。
空间复杂度:归并排序的空间复杂度为O(n),其中n是待排序序列的长度。它需要额外的存储空间来存放归并操作的中间结果,但相对于快速排序的原地排序特性,归并排序的空间复杂度较高。
对于链表排序的适用性:由于归并排序的特点是通过递归将待排序序列划分为多个子序列进行排序,然后再合并这些有序的子序列,所以对于链表这种数据结构,归并排序是一种更加适合的排序算法。
易于并行化实现:归并排序的合并操作可以很容易地并行化实现。可以将待排序序列划分为多个子序列,在多个处理器上并行地进行排序和合并操作,从而提高排序的效率。
综上所述,归并排序具有稳定性、较低的时间复杂度、对链表排序的适用性以及易于并行化实现等优点。在本问题中,采用归并排序可以按赛事类别有序输出参赛团队的基本信息,满足问题的要求。
3.3设计思路
数据结构:使用TeamManagement类来管理参赛队伍的信息,其中使用List<Team>来存储所有队伍。
添加参赛队伍:addTeam方法用于向参赛队伍列表中添加队伍。在添加前,通过isTeamExist方法检查队伍是否已存在。
根据学校查询参赛团队:getTeamsBySchool方法用于根据学校名称查询参赛团队。遍历所有队伍,将符合学校名称的队伍加入结果列表,并返回结果。
根据赛事类别查询参赛团队:getTeamsByKind方法用于根据赛事类别查询参赛团队。遍历所有队伍,将符合赛事类别的队伍加入结果列表,并返回结果。
归并排序:mergeSortTeamsByKind方法用于按赛事类别对参赛团队进行归并排序。采用分治策略,将队伍列表不断划分为更小的子列表,然后通过mergeTeamsByKind方法进行合并排序。最终得到按赛事类别有序的参赛团队列表。
归并操作:mergeTeamsByKind方法用于将两个有序的子列表按赛事类别进行合并。创建临时的左右子列表,逐个比较赛事类别,并将较小的队伍放入原始列表中。最后将剩余的队伍依次放入原始列表。
3.4代码
package com.it.DS;
import java.util.ArrayList;
import java.util.List;
public class TeamManagement {
public List<Team> teams;
public TeamManagement() {
teams = new ArrayList<>();
}
public boolean isTeamExist(Team team) {
for (Team existingTeam : teams) {
if (existingTeam.getSerial_number() == team.getSerial_number()) {
return true;
}
}
return false;
}
public void addTeam(Team team) {
if (!isTeamExist(team)) {
teams.add(team);
}
}
public List<Team> getTeamsBySchool(String school) {
List<Team> result = new ArrayList<>();
for (Team team : teams) {
if (team.getSchool().equals(school)) {
result.add(team);
}
}
return result;
}
public List<Team> getTeamsByKind(String kind) {
List<Team> result = new ArrayList<>();
for (Team team : teams) {
if (team.getKind().equals(kind)) {
result.add(team);
}
}
return result;
}
public void mergeSortTeamsByKind(List<Team> teams, int low, int high) {
if (low < high) {
int mid = (low + high) / 2;
mergeSortTeamsByKind(teams, low, mid);
mergeSortTeamsByKind(teams, mid + 1, high);
mergeTeamsByKind(teams, low, mid, high);
}
}
public void mergeTeamsByKind(List<Team> teams, int low, int mid, int high) {
int leftSize = mid - low + 1;
int rightSize = high - mid;
List<Team> leftTeams = new ArrayList<>();
List<Team> rightTeams = new ArrayList<>();
for (int i = 0; i < leftSize; i++) {
leftTeams.add(teams.get(low + i));
}
for (int i = 0; i < rightSize; i++) {
rightTeams.add(teams.get(mid + 1 + i));
}
int i = 0, j = 0, k = low;
while (i < leftSize && j < rightSize) {
Team leftTeam = leftTeams.get(i);
Team rightTeam = rightTeams.get(j);
if (leftTeam.getKind().compareTo(rightTeam.getKind()) <= 0) {
teams.set(k, leftTeam);
i++;
} else {
teams.set(k, rightTeam);
j++;
}
k++;
}
while (i < leftSize) {
teams.set(k, leftTeams.get(i));
i++;
k++;
}
while (j < rightSize) {
teams.set(k, rightTeams.get(j));
j++;
k++;
}
}
}
3.5结果展示
4.叫号系统
4.1问题描述
为省赛现场设计一个决赛叫号系统。所有参赛队按赛事组织文件中的赛事类别分到9个决赛室,决赛室按顺序叫号,被叫号参赛队进场,比赛结束后,下一参赛队才能进赛场。请模拟决赛叫号系统,演示省赛现场各决赛室的参赛队进场情况。(模拟时,要能直观展示叫号顺序与进场秩序一致)
4.2问题分析
数据结构:使用栈(stack)作为决赛叫号系统的数据结构,用于存储待进场的参赛队伍。栈的特点是先进后出,符合参赛队伍进场的顺序要求。
叫号系统流程:创建一个栈,用于存储待进场的参赛队伍。对赛事类别进行遍历,按顺序依次处理每个赛事类别的参赛队伍。遍历当前赛事类别的参赛队伍,将其依次压入栈中,保证栈顶的队伍是下一个待进场的队伍。每当一支队伍完成比赛并离场后,从栈顶取出下一个待进场的队伍。模拟输出每个决赛室的参赛队伍进场情况,按照叫号顺序与实际进场顺序保持一致。
示例演示:假设有9个决赛室,按顺序编号为1到9。遍历赛事类别,依次处理每个赛事类别的参赛队伍。对于当前赛事类别,遍历参赛队伍,并将其依次压入栈中。从栈中取出队伍,进入对应的决赛室进行比赛。输出每个决赛室的参赛队伍进场情况,保证叫号顺序与实际进场顺序一致。当一支队伍完成比赛并离场后,再从栈中取出下一个队伍进场。
4.3设计思路
数据结构:使用一个数组 rooms 来存储每个决赛室的栈。每个元素 rooms[i] 表示第 i+1 个决赛室对应的栈。栈的特点是先进后出,符合参赛队伍进场的顺序要求。
叫号系统流程:首先,根据决赛室的数量创建 NUM_ROOMS 个栈,分别存储每个决赛室的参赛队伍。初始时,将第一支队伍压入第一个决赛室的栈中。进入循环,遍历所有参赛队伍:计算当前队伍所属决赛室的编号roomNumber,采用取余操作 (i % NUM_ROOMS)。获取当前决赛室的栈 currentRoom。如果当前决赛室的栈不为空,表示有队伍待进场,进行叫号并出栈。输出当前叫号队伍的信息,包括决赛室编号和参赛队伍的名称。如果还有队伍未进场,则将下一支参赛队伍加入对应决赛室的栈中。循环结束后,模拟叫号过程完成。
4.4代码
package com.it.DS;
import java.util.Stack;
public class FinalCallSystem {
public static final int NUM_ROOMS = 9; // 决赛室数量
public static void Call(Team[] teams){
// 将参赛队按照赛事类别分配到决赛室的栈中
Stack<Team>[] rooms = new Stack[NUM_ROOMS];
for (int i = 0; i < NUM_ROOMS; i++) {
rooms[i] = new Stack<>();
}
rooms[0].push(teams[0]);
// 模拟叫号过程
for (int i = 0; i < teams.length; i++) {
int roomNumber = i % NUM_ROOMS; // 当前叫号的决赛室编号
Stack<Team> currentRoom = rooms[roomNumber];
// 如果当前决赛室还有队伍未进场,则叫号并进场
if (!currentRoom.isEmpty()) {
Team team = currentRoom.pop();
System.out.println("决赛室 " + (roomNumber + 1) + ":参赛队 " + team.getName() + " 进场");
}
// 如果还有队伍未进场,则将下一参赛队伍加入对应决赛室的栈中
if (i < teams.length - 1) {
int nextRoomNumber = (roomNumber + 1) % NUM_ROOMS; // 下一决赛室编号
Stack<Team> nextRoom = rooms[nextRoomNumber];
nextRoom.push(teams[i + 1]);
}
}
}
}
4.5结果展示
5.地图导航
5.1问题描述
赛事系统为参赛者提供赛地的校园导游程序,为参赛者提供各种路径导航的查询服务。以我校长山校区提供比赛场地为例,(请为参赛者提供不少于10个目标地的导航。可为参赛者提供校园地图中任意目标地(建筑物)相关信息的查询;提供任意两个目标地(建筑物)的导航查询,即查询任意两个目的地(建筑物)之间的一条最短路径。
5.2问题分析
数据结构设计:使用图来表示校园地图,其中每个建筑物或目标地为图的一个节点,路径为图的边。每个节点包含建筑物的相关信息,如名称、位置、介绍等。
构建图:根据题目要求,在校园中选择不少于10个目标地作为图的节点。为每个节点添加邻居节点,表示节点之间的路径。为节点之间的边赋予权重,可以根据实际情况设定,如距离、时间等。
导航查询服务:提供任意两个目标地(建筑物)之间的最短路径查询。
使用 Dijkstra 算法来计算最短路径。
Dijkstra 算法基本步骤:初始化距离数组和访问数组,记录起始节点到其他节点的距离,并将起始节点标记为已访问。从起始节点开始遍历邻居节点,更新距离数组,选择距离最短且未访问过的节点作为下一个节点。重复上述步骤,直到所有节点都被访问过或找到目标节点。根据距离数组和路径信息,得到最短路径。
查询服务实现:根据参赛者提供的目的地,查询目的地的相关信息,如建筑物名称、位置、介绍等。提供任意两个目标地之间的最短路径查询功能,根据起点和终点节点,使用 Dijkstra 算法计算最短路径。输出最短路径,包括节点顺序和对应的路径距离。
5.3设计思路
CampusTourGuide类是主要实现校园导游程序的类。它包含了地点信息的存储和最短路径查询的功能。在构造函数中,它初始化了地点信息和图的数据结构。
当地点数量较多时,可以使用CampusTourGuide类中的initializeLocations方法来初始化地点信息,将地点名称和对应的Location对象存储在locations字典中。这样,我们可以通过地点名称快速查找地点信息。
使用initializeGraph方法初始化图的数据结构。它调用Graph类的addEdge方法来添加路径(边),指定起始地点、目标地点和路径的权重。这样,我们就可以建立地点之间的连接关系。
getLocationInfo方法接受一个地点名称作为参数,并返回该地点的相关信息。它通过在locations字典中查找地点名称对应的Location对象,然后返回该对象。这样,我们可以根据地点名称快速获取地点的描述信息。
getShortestPath方法接受两个地点名称作为参数,返回从起始地点到目标地点的最短路径。它通过调用Graph类的getShortestPath方法实现最短路径的计算。如果找到了最短路径,将其返回;否则返回一个空列表。这样,我们可以根据起始地点和目标地点快速获取它们之间的最短路径。
getShortestPath方法使用Dijkstra算法来计算从起始地点到目标地点的最短路径。它使用优先队列priorityQueue来选择下一个要探索的节点,使用distances字典记录每个地点的最短距离,使用previousNodes字典记录每个地点的前驱节点。
Graph类是实现最短路径算法的关键。它使用邻接列表来表示地点之间的连接关系。其中,adjacencyList是一个字典,将地点名称映射到包含边的列表。在算法开始时,将起始地点的距离设置为0,并将其添加到优先队列中。将其他地点的距离设置为无穷大,并添加到优先队列中。然后,开始循环直到优先队列为空。在每次循环中,从优先队列中取出一个节点,如果该节点是目标地点,则找到了最短路径。使用previousNodes字典回溯路径,并将路径逆序添加到shortestPath列表中。最后,将起始地点添加到shortestPath列表的开头,然后返回最短路径。
addEdge方法接受起始地点、目标地点和路径权重作为参数,将路径添加到邻接列表中。首先,它创建一个Edge对象,表示路径的目标地点和权重。然后,通过调用computeIfAbsent方法,将起始地点和目标地点添加到邻接列表中,并将Edge对象添加到对应地点的边列表中。
getShortestPathLength方法根据最短路径获取路径长度。首先,调用getShortestPath方法获取最短路径。如果找到了最短路径,遍历路径上的每一条边,累加路径长度,并将其返回。如果没有找到最短路径,返回-1表示无法找到路径。
locations是一个Map,用于存储地点名称和对应的Location对象。
Location类用于表示校园地点,包含了地点的名称和描述信息。
Edge类表示路径(边),包含了目标地点和权重。
Node类实现了Comparable接口,用于在Dijkstra算法中比较节点的距离。它包含了地点名称和距离属性。
5.4代码
package com.it.DS;
import java.util.*;
//校园导游程序的主要功能在CampusTourGuide类中实现,包括初始化地点和路径,提供查询地点信息和查询最短路径的功能
public class CampusTourGuide {
public Map<String, Location> locations;
public Graph graph;
public CampusTourGuide() {
locations = new HashMap<>();
graph = new Graph();
initializeLocations();
initializeGraph();
}
public void initializeLocations() {
// 添加校园地点(建筑物)
locations.put("3号组团", new Location("3号组团", "这是学生宿舍区,提供住宿。"));
locations.put("西食堂", new Location("西食堂", "这是西食堂,提供各种美食。"));
locations.put("明德园", new Location("明德园", "这是明德园,用于散步的地方。"));
locations.put("文体中心", new Location("文体中心", "这是文体中心,可以体育锻炼。"));
locations.put("西操场", new Location("西操场", "这是西操场,适合体育锻炼。"));
locations.put("文理大楼", new Location("文理大楼", "这是教学楼,用于上课的地方。"));
locations.put("雕塑", new Location("雕塑", "这是雕塑,供欣赏。"));
locations.put("求索园", new Location("求索园", "这是求索园,用于散步的地方。"));
locations.put("东食堂", new Location("东食堂", "这是东食堂,提供各种美食。"));
locations.put("图书馆", new Location("图书馆", "这是图书馆,你可以在这里借阅书籍。"));
}
public void initializeGraph() {
// 添加路径(边)
graph.addEdge("3号组团", "西食堂", 100);
graph.addEdge("3号组团", "文体中心", 200);
graph.addEdge("西食堂", "明德园", 80);
graph.addEdge("西食堂", "文体中心", 150);
graph.addEdge("明德园", "西操场", 120);
graph.addEdge("明德园", "文理大楼", 110);
graph.addEdge("文体中心", "西操场", 50);
graph.addEdge("西操场", "求索园", 150);
graph.addEdge("西操场", "东食堂", 230);
graph.addEdge("文理大楼", "雕塑", 80);
graph.addEdge("文理大楼", "求索园", 60);
graph.addEdge("雕塑", "图书馆", 100);
graph.addEdge("求索园", "东食堂", 90);
graph.addEdge("求索园", "图书馆", 70);
graph.addEdge("东食堂", "图书馆", 50);
}
//getLocationInfo方法查询特定地点的相关信息
public Location getLocationInfo(String locationName) {
return locations.get(locationName);
}
//getShortestPath方法查询任意两个目标地之间的最短路径
public List<String> getShortestPath(String startLocation, String endLocation) {
List<String> shortestPath = graph.getShortestPath(startLocation, endLocation);
if (shortestPath == null) {
return Collections.emptyList();
} else {
return shortestPath;
}
}
}
//校园地点使用Location类表示
class Location {
public String name;
public String description;
public Location(String name, String description) {
this.name = name;
this.description = description;
}
public String getName() {
return name;
}
public String getDescription() {
return description;
}
}
//Graph类实现了最短路径算法(Dijkstra算法)来查找任意两个目标地之间的最短路径
class Graph {
public Map<String, List<Edge>> adjacencyList;
public Map<String, String> previousNodes = new HashMap<>();
public Graph() {
adjacencyList = new HashMap<>();
}
public void addEdge(String source, String destination, int weight) {
Edge edge = new Edge(destination, weight);
adjacencyList.computeIfAbsent(source, k -> new ArrayList<>()).add(edge);
adjacencyList.computeIfAbsent(destination, k -> new ArrayList<>()); // 确保目标位置有条目
}
public List<String> getShortestPath(String start, String end) {
PriorityQueue<Node> priorityQueue = new PriorityQueue<>();
Map<String, Integer> distances = new HashMap<>();
Map<String, String> previousNodes = new HashMap<>();
for (String location : adjacencyList.keySet()) {
if (location.equals(start)) {
distances.put(location, 0);
priorityQueue.add(new Node(location, 0));
} else {
distances.put(location, Integer.MAX_VALUE);
priorityQueue.add(new Node(location, Integer.MAX_VALUE));
}
previousNodes.put(location, null);
}
while (!priorityQueue.isEmpty()) {
Node currentNode = priorityQueue.poll();
String currentLocation = currentNode.getLocation();
if (currentLocation.equals(end)) {
List<String> shortestPath = new ArrayList<>();
while (previousNodes.get(currentLocation) != null) {
shortestPath.add(0, currentLocation);
currentLocation = previousNodes.get(currentLocation);
}
shortestPath.add(0, start);
return shortestPath;
}
if (distances.get(currentLocation) < currentNode.getDistance()) {
continue;
}
List<Edge> neighbors = adjacencyList.get(currentLocation);
if (neighbors != null) {
for (Edge neighbor : neighbors) {
int distance = distances.get(currentLocation) + neighbor.getWeight();
if (distance < distances.get(neighbor.getDestination())) {
distances.put(neighbor.getDestination(), distance);
previousNodes.put(neighbor.getDestination(), currentLocation);
priorityQueue.add(new Node(neighbor.getDestination(), distance));
}
}
}
}
return null;
}
public int getShortestPathLength(String start, String end) {
List<String> shortestPath = getShortestPath(start, end);
if (shortestPath == null) {
return -1; // 无法找到路径
} else {
int pathLength = 0;
for (int i = 0; i < shortestPath.size() - 1; i++) {
String currentLocation = shortestPath.get(i);
String nextLocation = shortestPath.get(i + 1);
List<Edge> edges = adjacencyList.get(currentLocation);
for (Edge edge : edges) {
if (edge.getDestination().equals(nextLocation)) {
pathLength += edge.getWeight();
break;
}
}
}
return pathLength;
}
}
}
//路径使用Edge类表示
class Edge {
public String destination;
public int weight;
public Edge(String destination, int weight) {
this.destination = destination;
this.weight = weight;
}
public String getDestination() {
return destination;
}
public int getWeight() {
return weight;
}
}
class Node implements Comparable<Node> {
public String location;
public int distance;
public Node(String location, int distance) {
this.location = location;
this.distance = distance;
}
public String getLocation() {
return location;
}
public int getDistance() {
return distance;
}
@Override
public int compareTo(Node other) {
return Integer.compare(distance, other.distance);
}
}
5.5结果展示
Tools工具类
package com.it.DS;
import java.io.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
public class Tools {
public static void add(String s) throws IOException {
FileWriter fw=new FileWriter("D:\\IDEA2022\\ds-2023-design\\team.txt",true);
fw.write(s);
fw.close();
}
public static void look() throws IOException {
FileReader fr=new FileReader(new File("D:\\IDEA2022\\ds-2023-design\\team.txt"));
int len;
while ((len=fr.read())!=-1){
System.out.print((char) len);
}
fr.close();
}
public static void delete(){
String filePath = "D:\\IDEA2022\\ds-2023-design\\team.txt";
Scanner sc=new Scanner(System.in);
System.out.print("请输入要删除的信息:");
String lineToRemove = sc.nextLine();
try {
File inputFile = new File(filePath);
File tempFile = new File("temp.txt");
BufferedReader reader = new BufferedReader(new FileReader(inputFile));
BufferedWriter writer = new BufferedWriter(new FileWriter(tempFile));
String currentLine;
while ((currentLine = reader.readLine()) != null) {
if (currentLine.equals(lineToRemove)) {
continue;
}
writer.write(currentLine);
writer.newLine();
}
writer.close();
reader.close();
if (inputFile.delete()) {
tempFile.renameTo(inputFile);
System.out.println("成功删除文件中的行文本。");
} else {
System.out.println("无法删除文件中的行文本。");
}
} catch (IOException e) {
e.printStackTrace();
}
}
public static String getString(Team team){
StringBuilder sb=new StringBuilder();
sb.append(team.serial_number+" # ");
sb.append(team.name+" # ");
sb.append(team.school+" # ");
sb.append(team.kind+" # ");
for (int i = 0; i < team.team_member.length; i++)
sb.append(team.team_member[i]+" # ");
sb.append(team.teacher);
String newData=sb.toString();
return newData;
}
public static void upData() throws IOException {
String filePath = "D:\\IDEA2022\\ds-2023-design\\team.txt";
try {
// 创建一个临时文件来保存修改后的内容
File tempFile = File.createTempFile("tempFile", ".txt");
// 创建文件输入流读取原始文件
FileReader fileReader = new FileReader(filePath);
BufferedReader bufferedReader = new BufferedReader(fileReader);
// 创建文件输出流写入修改后的内容
FileWriter fileWriter = new FileWriter(tempFile);
BufferedWriter bufferedWriter = new BufferedWriter(fileWriter);
Scanner scanner = new Scanner(System.in);
System.out.print("请输入要匹配的字符串(长度为10):");
String matchString = scanner.nextLine();
String line;
boolean firstLineSkipped = false;
while ((line = bufferedReader.readLine()) != null) {
if (!firstLineSkipped) {
// 跳过第一行
firstLineSkipped = true;
bufferedWriter.write(line);
bufferedWriter.newLine();
continue;
}
// 提取每行的前10个数据
String linePrefix = line.substring(0, 10);
if (linePrefix.equals(matchString)) {
// 匹配成功,修改该行数据
System.out.print("请输入要修改为的新数据:");
String newContent = scanner.nextLine();
line = line.replaceFirst(linePrefix, newContent);
}
bufferedWriter.write(line);
bufferedWriter.newLine();
}
// 关闭流
bufferedReader.close();
bufferedWriter.close();
fileReader.close();
fileWriter.close();
// 删除原始文件
File originalFile = new File(filePath);
if (originalFile.delete()) {
// 重命名临时文件为原始文件名
tempFile.renameTo(originalFile);
System.out.println("文件内容修改成功。");
} else {
System.out.println("无法删除原始文件。");
}
} catch (IOException e) {
e.printStackTrace();
}
}
public static BinarySearchTree zhuanHuaInsert() throws IOException {
String filePath = "D:\\IDEA2022\\ds-2023-design\\team.txt";
BinarySearchTree binarySearchTree=new BinarySearchTree();
try (BufferedReader reader = new BufferedReader(new FileReader(filePath))) {
String line;
boolean isFirstLine = true; // 标记是否是第一行
while ((line = reader.readLine()) != null) {
if (isFirstLine) {
isFirstLine = false;
continue; // 跳过第一行
}
// 将每行内容解析为Team对象
Team team = parseTeam(line);
binarySearchTree.insert(team);
}
} catch (IOException e) {
e.printStackTrace();
}
return binarySearchTree;
}
public static Team[] ZhuanHuaInsertStack() throws IOException {
String filePath = "D:\\IDEA2022\\ds-2023-design\\team.txt";
int lengthT=0;
ArrayList<Team> getT=new ArrayList<>();
try (BufferedReader reader = new BufferedReader(new FileReader(filePath))) {
String line;
boolean isFirstLine = true; // 标记是否是第一行
while ((line = reader.readLine()) != null) {
if (isFirstLine) {
isFirstLine = false;
continue; // 跳过第一行
}
// 将每行内容解析为Team对象
Team team = parseTeam(line);
lengthT++;
getT.add(team);
}
} catch (IOException e) {
e.printStackTrace();
}
Team[] teams=new Team[lengthT];
for (int i = 0; i < teams.length; i++) {
teams[i]=getT.get(i);
}
return teams;
}
public static TeamManagement zhuanHuaInsert2() throws IOException {
String filePath = "D:\\IDEA2022\\ds-2023-design\\team.txt";
TeamManagement teamManagement = new TeamManagement();
try (BufferedReader reader = new BufferedReader(new FileReader(filePath))) {
String line;
boolean isFirstLine = true; // 标记是否是第一行
while ((line = reader.readLine()) != null) {
if (isFirstLine) {
isFirstLine = false;
continue; // 跳过第一行
}
// 将每行内容解析为Team对象
Team team = parseTeam(line);
teamManagement.addTeam(team);
}
} catch (IOException e) {
e.printStackTrace();
}
return teamManagement;
}
public static Team parseTeam(String line) {
String[] parts = line.split("#");//将每行数据按 # 字符进行拆分。然后,解析每个拆分后的部分并去除前后的空格。
int serial_number = Integer.parseInt(parts[0].trim());
String name = parts[1].trim();
String school = parts[2].trim();
String kind = parts[3].trim();
char[] team_member = parts[4].trim().toCharArray();
String teacher = parts[5].trim();
return new Team(serial_number, name, school, kind, team_member, teacher);
}
public static void searchOne(CampusTourGuide tourGuide,String str){
// 查询单个目标地(建筑物)的相关信息
Location locationInfo = tourGuide.getLocationInfo(str);
if (locationInfo != null) {
System.out.println("目标地名称:" + locationInfo.getName());
System.out.println("目标地描述:" + locationInfo.getDescription());
} else {
System.out.println("该地点不存在。");
}
}
public static int searchShort(CampusTourGuide tourGuide,String startLocation,String endLocation){
// 查询任意两个目标地(建筑物)之间的最短路径
List<String> shortestPath = tourGuide.getShortestPath(startLocation, endLocation);
if (shortestPath.isEmpty()) {
System.out.println("无法找到从 " + startLocation + " 到 " + endLocation + " 的最短路径。");
} else {
System.out.println("从 " + startLocation + " 到 " + endLocation + " 的最短路径为:");
for (String location : shortestPath) {
System.out.print(location + " -> ");
}
System.out.println("到达目的地!");
}
int shortLength=tourGuide.graph.getShortestPathLength(startLocation, endLocation);
return shortLength;
}
}
main方法
import com.it.DS.*;
import java.io.IOException;
import java.util.List;
import java.util.Scanner;
import java.util.Stack;
public class Main {
public static void main(String[] args) throws IOException {
System.out.println("--------------------------------------------------欢迎使用赛事管理系统!---------------------------------------------");
Scanner sc=new Scanner(System.in);
System.out.println("请选择你需要的功能:");
System.out.println(" 1.管理队伍");
System.out.println(" 2.查找队伍");
System.out.println(" 3.叫号系统");
System.out.println(" 4.地图导航");
int op1=sc.nextInt();
if (op1==1){
System.out.println("请选择具体功能:");
System.out.println("1.增加队伍");
System.out.println("2.删除队伍");
System.out.println("3.修改队伍");
System.out.println("4.查看队伍");
int op2=sc.nextInt();
sc.nextLine();
if (op2==1){
System.out.println("输入你要添加的信息:");
String str=sc.nextLine();
Tools.add(str);
Tools.look();
} else if (op2==2) {
Tools.delete();
} else if (op2==3) {
Tools.upData();
} else if (op2==4) {
Tools.look();
}
}
else if (op1==2){
System.out.println("请选择具体功能:");
System.out.println("1.按编号查找");
System.out.println("2.按学校查找");
System.out.println("3.按类型查找");
System.out.println("4.按类型归并排序");
int op3=sc.nextInt();
sc.nextLine();
TeamManagement teamManagement = Tools.zhuanHuaInsert2();
if (op3==1){
BinarySearchTree bst = Tools.zhuanHuaInsert();
System.out.print("输入你要查找的编号:");
int num= sc.nextInt();
Team foundTeam = bst.find(num);
if (foundTeam != null) {
System.out.println("已找到队伍: " + foundTeam.toString());
System.out.println("平均查找长度ASL为: " +bst.calculateASL());
} else {
System.out.println("编号为:" + num + " 的队伍未找到");
}
} else if (op3==2) {
System.out.print("输入学校名称:");
String str=sc.nextLine();
List<Team> teamsBySchool = teamManagement.getTeamsBySchool(str);
for (Team team : teamsBySchool) {
System.out.println(team);
}
} else if (op3==3) {
System.out.print("输入类型:");
String str=sc.nextLine();
List<Team> teamsByKind = teamManagement.getTeamsByKind(str);
for (Team team : teamsByKind) {
System.out.println(team);
}
} else if (op3==4) {
teamManagement.mergeSortTeamsByKind(teamManagement.teams, 0, teamManagement.teams.size() - 1);
for (Team team : teamManagement.teams) {
System.out.println(team);
}
}
}
else if (op1==3){
System.out.println("叫号顺序及教室为:");
Team[] teams=Tools.ZhuanHuaInsertStack();
FinalCallSystem finalCallSystem=new FinalCallSystem();
finalCallSystem.Call(teams);
}
else if (op1==4){
System.out.println("请选择具体功能:");
System.out.println("1.查询地点信息");
System.out.println("2.查找最短路径");
int op4=sc.nextInt();
sc.nextLine();
CampusTourGuide campusTourGuide=new CampusTourGuide();
if (op4==1){
System.out.print("输入地点名称:");
String str=sc.nextLine();
Tools.searchOne(campusTourGuide,str);
} else if (op4==2) {
System.out.print("输入起点名称:");
String str=sc.nextLine();
System.out.print("输入终点名称:");
String str2=sc.nextLine();
System.out.println(Tools.searchShort(campusTourGuide,str,str2));
}
}
}
}