江苏科技大学 ——《数据结构课程设计》实验
目录
一、绪论
1.1 课程设计的目的
1.2 课程设计的内容
1.3 课程设计的环境
二、需求分析
2.1 问题描述
2.2 问题分析
2.3 算法设计
三、完整代码
一、绪论
1.1课程设计的目的
(1) 熟练掌握线性表、栈、队列、串、数组、树和图等基本数据结构的逻辑特性和存储表示方法;熟练掌握各种基本数据结构的基本算法和其应用;熟练掌握问题分析、数据结构设计、程序设计的基本技能和技术。
(2) 能够综合运用数据结构与算法和相关的数学等理论知识对复杂工程中的算法问题进行抽象、分析和建模;能够依据工程实际问题的需求合理组织数据、并在计算机中有效地存储数据;能够针对复杂工程中的算法问题,设计出比较合理的解决方案,利用具体的编程语言实现解决方案,并具有一定的创新思维能力。
(3) 具有良好的工程素养和职业素养,诚信守法,能够坚持职业操守和道德规范;具有精益求精的工匠精神、创新精神和探索未知终身学习的意识;具有科技报国的社会责任感、使命感和爱国主义情操。
1.2课程设计的内容
中国大学生计算机设计大赛是我国高校面向本科生的计算机应用设计大赛,大赛旨在激发学生学习计算机知识和技能的兴趣与潜能,提高学生运用信息技术解决实际问题的综合能力。通过大赛这种计算机教学实践形式,可展示师生的教与学成果,最终以赛促学,以赛促教,以赛促创。该赛事在历届学生中影响力较大,参与者众多,请结合2021届省赛参赛的数据,借助数据结构课程所学的相关知识,通过对数据的处理和分析,熟悉数据结构设计及数据处理在信息管理系统中应用的重要性。赛事相关数据存储在文本文件和excel文件中,相应的文件信息说明如表1所示。其中,各个文件中不同的数据项之间均使用#分隔,如下图所示。
1.3 课程设计的环境
对信息资源进行收集、存储、处理、管理和利用,以实现信息资源的高效利用和信息安全的管理。信息管理系统在现代社会中越来越受到重视,因为电子信息资源的管理和利用可以提高组织的工作效率、降低成本、提高竞争力,同时也能够促进社会的信息化和数字化发展。借助数据结构课程所学的相关知识,通过对数据的处理和分析,熟悉数据结构设计及数据处理在信息管理系统中应用的重要性。
本次课程设计信息管理系统采用的是 IntelliJ IDEA 编写程序。
二、需求分析
2.1 问题描述
本次课程设计要求协助中国大学生计算机设计大赛江苏省组委会,设计一款赛事管理系统,实现赛务相关的数据管理及信息服务,该系统能够为省级赛事管理解决以下问题:
(1)能够管理各参赛队的基本信息(包含参赛队编号,参赛作品名称,参赛学校,赛事类别,参赛者,指导老师),赛事类别共11项(参见大赛官网jsjds.blcu.edu.cn);包括增加、删除、修改参赛队伍的信息。
(2)从team.txt中读取参赛队伍的基本信息,实现基于二叉排序树的查找。根据提示输入参赛队编号,若查找成功,输出该赛事类别对应的基本信息(参赛作品名称、参赛学校、赛事类别、参赛者和指导老师信息),同时,输出查找成功时的平均查找长度ASL;否则,输出“查找失败!”。
(3)能够提供按参赛学校查询参赛团队(或根据赛事类别查询参赛团队),即,根据提示输入参赛学校名称(赛事类别),若查找成功,输出该学校参赛的(该赛事类别的)所有团队的基本信息,输出的参赛团队按赛事类别有序输出。(排序算法可从选择排序、插入排序、希尔排序、归并排序、堆排序中任意选择,并为选择算法的原因做出说明。)
(4)为省赛现场设计一个决赛叫号系统。所有参赛队按赛事组织文件中的赛事类别分到9个决赛室,决赛室按顺序叫号,被叫号参赛队进场,比赛结束后,下一参赛队才能进赛场。请模拟决赛叫号系统,演示省赛现场各决赛室的参赛队进场情况。(模拟时,要能直观展示叫号顺序与进场秩序一致)
(5)赛事系统为参赛者提供赛地的校园导游程序,为参赛者提供各种路径导航的查询服务。以我校长山校区提供比赛场地为例,(请为参赛者提供不少于10个目标地的导航。可为参赛者提供校园地图中任意目标地(建筑物)相关信息的查询;提供任意两个目标地(建筑物)的导航查询,即查询任意两个目的地(建筑物)之间的一条最短路径。
2.2 问题分析
2.2.1 参赛队伍管理
首先这是一个信息管理系统问题,为了实现参赛队伍信息的管理和查询,需要定义选择一个合适的数据结构来存储参赛队伍的信息。常见的数据结构包括数组、链表、树和图等,每种数据结构都有其优缺点和适用范围,需要根据实际需求进行选择。
任务中,需根据用户需求,进行操作选择的控制,即可以通过Switch-Case,进行选择。该程序需要对用户输入的数据进行录入、查询、修改和删除操作,因此需要优化程序的性能,以提高响应速度和用户体验。在优化程序性能时,可以考虑使用多线程、缓存和分布式等技术,以提高程序的并发性和可扩展性。而数据的存储方式,则选择ArrayList数组进行存储,ArrayList可以随时对其中的元素进行修改、添加、删除,方便操作。
针对这一项目,将团队信息的元素信息,例如参赛队伍ID、参赛作品名称等归并到一个类中,以该类作为ArrayList存储容器,进行问题的实现。
对于数据读取的过程,由于数据是将存储到txt文档读取出来的,故需额外设计读取设计的操作,且由于在对队伍进行操作修改、增添、删除时,txt文档的数据也需进行修改,故对此需额外设计相应操作。
2.2.2 基于二叉排序树的查找
为了实现二叉排序树,需要定义一个节点类来表示参赛队伍的信息,并实现插入、删除、查找等操作。在实现数据结构时,需要考虑节点的存储方式、查找算法的时间复杂度等问题。
在实现查找功能时,需要采用基于二叉排序树的查找算法。该算法的时间复杂度为 O(log n),但在实际应用中可能会出现查找性能略低于最优解的情况。因此,在实验过程中需要多次进行查找操作,统计平均查找长度 ASL,并与算法最优解进行比较。
在实现二叉排序树时,节点的存储方式是非常重要的。如果选择不当,可能会导致节点的数据结构不合理,从而影响查找性能。一般来说,可以选择数组、链表、哈希表等方式来存储节点。
在二叉排序树中,每个节点都有一个唯一的编号。在实现数据结构时,需要确保编号的分配合理,并且编号范围足够大,以避免出现编号冲突的问题。在实验过程中,需要对数据结构进行适当的维护。例如,在插入、删除节点时,需要保证数据结构的一致性和完整性。否则,可能会导致实验结果不准确,或者查找性能下降。在实现数据结构时,需要确保数据结构的兼容性。具体来说,需要确保不同数据结构之间的数据可以正确交换和传递,以便实现数据的共享和处理。
在实验过程中,需要对实验结果进行认真的分析和总结。具体来说,需要统计实验过程中查找成功的次数、平均查找长度 ASL 以及查找失败的次数等指标,并根据实验结果分析算法的优缺点和适用范围。
2.2.3 参赛团队查询
在任务一中,已经完成了团队信息的数据存储读取,并成功建立了存储团队信息的数据结构类:Team,在此任务中,仅需思考的问题,只有如何按照条件要求,进行查找。
任务提供了多个排序算法的使用,由于查询的方式是按照参赛学校名称,或者是参赛的赛事类别,需进行一一比对,将每个元素进行比较读取,所以选择了“选择排序”,作为该任务的算法使用。通过调试代码,发现选择排序的查找方式是正确的,能够成功查找到输入的学校名称对应的参赛团队。该排序算法,编写简单,同时满足条件的使用。虽然查找方式正确,但是选择排序的时间复杂度为 O(n^2),效率较低。因此,在实际应用中,需要考虑使用更高效的查找方式,如二分查找或哈希查找等。
2.2.4 决赛叫号模拟
本次实验的内容是一个决赛叫号系统,用于省赛现场。该系统将根据赛事组织文件中的赛事类别,将所有参赛队分为 9 个决赛室,并按照顺序叫号,被叫号参赛队进场,比赛结束后,下一参赛队才能进赛场。
根据文件内容的数据分析,赛事类别一共有31个,即每个赛事类别不可能能单独得到一个决赛室,故需要将其考虑合理将31个赛事类别均分到9个决赛室。
同时,在该任务中,要求每个赛事类别的参赛队伍按照参赛队伍的ID,依次有序的进行叫号,即需考虑在查找到相关赛事类别的参赛队伍时,需将队伍额外存储在一个存储容器中,当查询完该赛事类别后,将存储容器中的元素按照参赛队伍ID进行有序输出。需要明确叫号系统的具体需求和功能,例如参赛队如何分组、如何按照顺序叫号、如何验证参赛队信息等。存储容器可选择Map,ArrayList,Queue等进行存储。
2.2.5 校园导航
这是一个图论问题,而且校园内道路一般是双向通行的,所以这是一个无向图。对于图的存储结构而言,图中各个景点的存储结构有邻接表和邻接矩阵两种存储结构,考虑到顶点个数仅为10个,所以邻接表和邻接矩阵的复杂度相同。本题中选择使用邻接矩阵来表示图。
需要明确校园导游程序的具体需求和功能,例如提供校园地图中任意目标地 (建筑物) 相关信息的查询和任意两个目的地之间的一条最短的简单路径查询服务等。
任务中要求求解出图中景点的问路查询,即为给定两个源点,求解出两个顶点之间的最短路径。根据数据结构课程所学知识,有多种经典算法可以解决最短路径问题,包括Dijkstra算法,Floyd-Warshell算法,Bellman-Ford算法和深度优先遍历。不同是算法有不同的算法复杂度,考虑到校园中道路没有负权边,即算法均可解决最短路径问题。需要选择合适的数据结构和算法来实现校园导游程序的功能,例如使用地图对象、地点对象等来存储和处理校园地图数据,使用 Dijkstra 算法来计算任意两个目的地之间的最短路径等。
其中Dijkstra算法求的是单源最短路径:即从一个结点出发到其它所有结点的最短路径,算法的时间复杂度为O(n2),但题目要求任意两个结点的最短路径,所以还是要在外层增加一个循环,以求得多源最短路径。
在实现校园导游程序时,需要考虑安全和稳定性方面的问题,例如防止用户输入错误地址或恶意地址等,确保程序的稳定性和安全性。
2.3 算法设计
2.3.1 参赛队伍管理
团队信息的数据结构:类——Team,用于存储一个团队的详细信息,如参赛队伍ID、参赛作品名称等,该类还提供了相应的setter和getter。
public class Team { //基本信息
int teamID; //参赛队编号
String workName; //参赛作品名称
String university; //参赛学校
String eventCate; //赛事类别 赛事类别共31项
String participant; //参赛者
String teacher; //指导老师
public Team(int teamID,String workName,String university,String eventCate,String participant,String teacher){ //构造方法
this.teamID = teamID;
this.workName = workName;
this.university = university;
this.eventCate = eventCate;
this.participant = participant;
this.teacher = teacher;
}
}
下面是文件读取的数据结构:前面提过由于数据是从文件中进行读取,并且需额外对此进行修改、添加、删除。采用File、BufferReader等进行文件的操作。定义一个新的数据结构类:FileManage——管理文件的操作内容,进行封装。
public class FileManage {
static String filePath = "D://IDEA/Code/ParticipateTeam/team.txt"; //文件路径
static String imagePath = "D://IDEA/Code/ParticipateTeam/map.png"; //图片路径
ArrayList<Team> teams; //用于执行存储,修改的操作
String content; //文件内容
FileManage(){ //构造方法
teams = new ArrayList<>();
dataStorage();
}
public String getFileRead(String filePath) //读取文本
public void setFileWrite(String filePath) //修改文件
public void dataStorage() //将读取的文件数据进行存储
public void fileDelete(String filePath,int ID) //文件删除
public void getImage(String imagePath) //读取图片
}
2.3.2 基于二叉排序树的查找
任务中,要求采用二叉排序树来实现参赛队伍信息的存储和管理。二叉排序树是一种特殊的二叉树,其中每个节点最多有两个子节点,一个左子节点和一个右子节点,或者没有子节点。
为了实现二叉排序树,我们首先需要定义一个节点类——Node,用于表示参赛队伍的信息。节点类需要包含参赛队编号、参赛作品名称、参赛学校、赛事类别、参赛者和指导老师等属性,即采用任务一中的Team作为元素属性使用即可。每一个节点存储一个队伍信息,然后可以使用递归算法来实现二叉排序树的构建和插入操作。另外该类还提供了相应的getter和setter并在构造方法中初始化了Node节点中的各种信息。
public class Node {
static int times = 0;
int teamID; //参赛队伍ID
Team team; //参赛队伍信息
Node left; //左子节点
Node right; //右子节点
public Node() { //构建函数
}
public Node(int teamID) { //构建函数
this.teamID = teamID;
}
public Node(int id,Team team){ //构建函数
this.teamID = id;
this.team = team;
}
public void setTeamName(int id){
this.teamID = id;
}
public int getTeamID(){
return this.teamID;
}
public Team getTeam() {
return team;
}
public void setTeam(Team team) {
this.team = team;
}
public int getTimes(){
return times;
}
在实现查找功能时,根据输入的参赛队编号,进行对应值的查找,构建相应的二叉排序树,构建二叉排序树的数据结构类:BinarySortTree。首先是从根节点开始进行查找,如果查找到的节点对应的参赛队编号等于输入的参赛队编号,则返回该节点所代表的赛事类别以及相关信息。否则,表示查找失败,输出“查找失败!”。另外该类还提供了相应的getter和setter并在构造方法中初始化了二叉树中的各种信息。
public class BinarySortTree {
Node root; //节点
public Node getRoot() {
return root;
}
public Node search(int value) //查找结点
public Node searchParent(int value) //查找父节点
public void delNode(int value) //删除节点
public void add(Node node) //添加节点
而对于平均查找长度 ASL,其查找成功的平均查找长度为:∑(本层高度*本层元素个数)/节点总数。即在二叉树查找的过程中,若查找成功,需计算出其所在层数高度,才能得出平均查找长度。
double asl = bTree.root.BST_ASL(bTree.root,0);
System.out.println(asl/teams.size());
ASL = ∑(本层高度*本层元素个数)/节点总数
2.3.3 参赛团队查询
由于查询的方式,有按照参赛学校名称和参赛的赛事类别,所以需额外声明一个变量,用于区分查询方式,即通过Switch-case将两种查找方式分开。
switch (type){
case 1: //1 为学校名称
if (team.getUniversity().equals(content)){
teamList.add(team);
System.out.println(team.getWorkName());
isFind = true;
}
break;
case 2: //2 为赛事类别
if (team.getEventCate().equals(content)){
teamList.add(team);
isFind = true;
}
break;
}
在for循环中进行一一比较,匹配是否与所查找的内容相匹的数据,匹配成功则输出显示该团队的详细信息,若无队伍信息与所查找的内容匹配,则输出“无信息匹配”。
2.3.4 决赛叫号模拟
叫号系统首先将赛事类别列举出来,方便使用查找,定义为静态全局变量,共31类。
static int eventNum = 31; //赛事类别 目前一共有31个
static String[] eventName = {"大数据实践", "信息图形设计", "动态信息影像(MG动画)", "交互信息设计",
"数据可视化","人工智能实践赛","Web应用与开发","管理信息系统","算法设计与应用",
"移动应用开发","移动应用开发(非游戏类)", "医药卫生","数字生活","运动健身",
"城市管理","行业应用","动画", "纪录片", "数字短片","微电影","新媒体漫画","环境设计",
"平面设计","产品设计", "交互媒体设计","游戏设计","虚拟现实VR与增强现实AR","汉语言文学",
"计算机基础与应用类课程微课", "虚拟实验平台","中、小学数学或自然科学课程微课"};
参赛队按照赛事组织文件中的赛事类别被分为 9 个决赛室,决赛室按照顺序叫号,被叫号参赛队进场,比赛结束后,下一参赛队才能进赛场,参赛队进场时,需要验证参赛队信息,确保参赛队身份准确无误。而在任务三中,完成了通过赛事类别进行查找参赛队伍,因此可以直接按照参赛类别将参赛队伍中的信息分开处理。即
//根据参赛类别 得到相应参赛队伍
static public ArrayList<Team> getTeamData(ArrayList<Team> teams,String content,int type)
//叫号系统
static public void callSystem(ArrayList<Team> teams){
for (int i=0;i<eventName.length;i++) {
String event = eventName[i];
ArrayList<Team> teamList = getTeamData(teams,event,2);
}
任务要求,同一类的参赛队伍,需按照参赛队伍ID有序依次排列进行,故选用Queue队列进行存储,同时使用Comparator组件,将存入的元素按照参赛队伍的ID有序存储,再依次从首端取出,得到有序的参赛队伍信息。
Queue<Team> q1 = new PriorityQueue<Team>(com);
for (Team team : teamList) {
q1.add(team); //其中按照ID大小 从小到大 按顺序排列
}
static Comparator<Team> com = new Comparator<Team>() {
@Override
public int compare(Team o1, Team o2) {
return o1.teamID-o2.teamID; //按照 团队ID 进行排列 升序
}
};
2.3.5 校园导航
节点信息的数据结构:类——VertexType,用于存储各个节点(地点)的详细信息,即地点编号、地点名称,该类还提供了相应的setter和getter。
public class VertexType { 顶点结构
int placeID; //定点序号
String placeName; //顶点名字
public VertexType(int placeID,String placeName) //构造方法
{
this.placeID = placeID;
this.placeName = placeName;
}
public int getPlaceID() {
return placeID;
}
public void setPlaceID(int placeID) {
this.placeID = placeID;
}
public String getPlaceName() {
return placeName;
}
public void setPlaceName(String placeName) {
this.placeName = placeName;
}
}
对于图的数据结构,构建类——Graph,用于将图表示成领接矩阵,并且是无向图存储方式进行存储,图一共有10个节点,即构建的二维数组为10*10的矩阵,同时因为是无向图,二维数组为对称矩阵,可以充分利用该特点进行运算。
public class Graph {
//根据图 得出的已知默认条件
static int ver = 10;
static int edg = 13;
static String[] places = {"北门","北苑食堂","计算机学院","文理大楼","图书馆",
"住宿区","笃学楼","东苑食堂","体育中心","南门"};
static int nullPoint = 1024;
int vertexNum,edgings; //图的 点数 、 边数
VertexType[] vertexTypes = new VertexType[10]; //定义10个顶点
int [][] distance = new int[10][10]; //用来存取权值 (地图上两个地点之间的距离)
//初始化数据
public Graph() //改进方式 将数据填写在文档中进行读取
{
vertexNum = ver;
edgings = edg;
for (int i=0;i<vertexNum;i++){ //地点名称存储
vertexTypes[i] = new VertexType(i+1,places[i]);
}
//地点的地理位置距离 存储
distance[0][1] = 100; //北门
distance[0][2] = 200;
distance[1][0] = 100; //北苑食堂
distance[1][4] = 400;
distance[2][0] = 200; //计算机学院
distance[2][3] = 100;
distance[3][2] = 100; //文理大楼
distance[3][5] = 550;
distance[3][6] = 300;
distance[4][1] = 400; //图书馆
distance[4][6] = 150;
distance[4][7] = 100;
distance[5][3] = 550; //住宿区
distance[5][8] = 450;
distance[6][3] = 300; //笃学楼
distance[6][4] = 150;
distance[6][8] = 500;
distance[6][9] = 200;
distance[7][4] = 100; //东苑食堂
distance[7][9] = 600;
distance[8][5] = 450; //体育中心
distance[8][6] = 200;
distance[8][9] = 250;
distance[9][6] = 200; //南门
distance[9][7] = 600;
distance[9][8] = 250;
//该二维矩阵为对称矩阵
//无边相连的点 距离设为 nullPoint 1024
for (int i=0;i<vertexNum;i++){
for (int n=0;n< vertexNum;n++){
if (distance[i][n] == 0) {
distance[i][n] = 1024;
}
}
}
}
Dijkstra 算法是一种贪心算法,用于计算图中任意两个节点之间的最短路径。在校园导游程序中,使用 Dijkstra 算法来计算任意两个目的地之间的最短路径,可以满足用户对于路径长度的优先级要求,即越短的路径越靠前。
public void ShortestPath_DIJ(Graph graph,int v0,int v1)
下面为校园景点的无向带权图:
三、完整代码
//FileManage.java
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Scanner;
public class FileManage {
static String filePath = "D://IDEA/Code/ParticipateTeam/team.txt"; //文件路径
static String imagePath = "D://IDEA/Code/ParticipateTeam/map.png"; //图片路径
ArrayList<Team> teams; //用于执行存储,修改的操作
String content;
FileManage(){
teams = new ArrayList<>();
dataStorage();
}
public void getContent(){
System.out.println(content);
}
public ArrayList<Team> getTeams(){
return teams;
}
public String getFileRead(String filePath) { //读取文本
StringBuilder lineTXT = new StringBuilder();
try{
File file = new File(filePath);
if (file.isFile() && file.exists()){
InputStreamReader inputStreamReader = new InputStreamReader(new FileInputStream(file), StandardCharsets.UTF_8);
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
String lineTXT_1;
while (((lineTXT_1 = bufferedReader.readLine()) != null)){
lineTXT.append(lineTXT_1).append("\n");
}
bufferedReader.close();
}
else {
System.out.println("TXT文档不存在");
}
}
catch (Exception e) {
System.out.println("ERROR,文件读取出现错误");
}
return lineTXT.toString();
}
public void setFileWrite(String filePath){
String str;
int num;
int teamID = 0;
String workName = null;
String university = null;
String eventCate = null;
String participant = null;
String teacher = null;
Scanner sc = new Scanner(System.in);
try {
FileWriter fileWriter = new FileWriter(filePath,true);
BufferedWriter bufferedWriter = new BufferedWriter(fileWriter);
System.out.println("输入需添加的参赛队编号");
num = sc.nextInt();
teamID = num;
bufferedWriter.write(num +"\t#\t");
System.out.println("输入需添加的参赛作品名称");
str = sc.next();
workName = str;
bufferedWriter.write(str+"\t#\t");
System.out.println("输入需添加的参赛学校");
str = sc.next();
university = str;
bufferedWriter.write(str+"\t#\t");
System.out.println("输入需添加的赛事类别");
str = sc.next();
eventCate = str;
bufferedWriter.write(str+"\t#\t");
System.out.println("输入需添加的参赛者");
str = sc.next();
participant = str;
bufferedWriter.write(str+"\t#\t");
System.out.println("输入需添加的指导教师");
str = sc.next();
teacher = str;
bufferedWriter.write(str+"\n");
bufferedWriter.close();
fileWriter.close();
} catch (Exception e) {
System.out.println("ERROR");
}
Team team = new Team(teamID,workName,university,eventCate,participant,teacher);
teams.add(team);
System.out.println("添加成功");
}
public void dataStorage() {
String txtReading = getFileRead(filePath); //获取文档信息 未删除空格
String txt = txtReading.replaceAll("\t",""); //去除空格
this.content = txt;
String[] resultLine = txt.split("\n");
for (int n=1;n<resultLine.length;n++) {
Team team = new Team();
String[] str = resultLine[n].split("#");
int i=0;
team.dataManaged(Integer.parseInt(str[i++]),str[i++],str[i++],str[i++],str[i++],str[i++]);
this.teams.add(team);
}
}
public void fileDelete(String filePath,int ID){
String str;
for (int i=0;i<teams.size();i++) {
if (teams.get(i).getTeamID() == ID){
teams.remove(i);
}
}
//将ArrayList 中的元素删除后重新添加到文件中
try {
FileWriter fileWriter = new FileWriter(filePath,true);
BufferedWriter bufferedWriter = new BufferedWriter(fileWriter);
bufferedWriter.write("参赛队编号\t#\t参赛作品名称\t#\t参赛学校\t#\t赛事类别\t#\t参赛者\t#\t指导教师\n");
for (Team team : teams) {
str = String.valueOf(team.getTeamID()) + "\t#\t" + team.getWorkName() + "\t#\t" +
team.getUniversity() + "\t#\t" + team.getEventCate() + "\t#\t" +
team.getParticipant() + "\t#\t" + team.getTeacher() +"\n";
bufferedWriter.write(str);
}
bufferedWriter.close();
fileWriter.close();
} catch (Exception e) {
System.out.println("ERROR");
}
System.out.println("删除成功");
}
public void getImage(String imagePath){
}
}
//Team.java
public class Team { //基本信息
int teamID; //参赛队编号
String workName; //参赛作品名称
String university; //参赛学校
String eventCate; //赛事类别 赛事类别共11项
String participant; //参赛者
String teacher; //指导老师
public Team (){
this.teamID=0;
this.workName=null;
this.university=null;
this.eventCate=null;
this.participant=null;
this.teacher=null;
}
public Team(int teamID,String workName,String university,String eventCate,String participant,String teacher){
this.teamID = teamID;
this.workName = workName;
this.university = university;
this.eventCate = eventCate;
this.participant = participant;
this.teacher = teacher;
}
public int getTeamID() {
return teamID;
}
public void setTeamID(int teamID) {
this.teamID = teamID;
}
public String getWorkName() {
return workName;
}
public void setWorkName(String workName) {
this.workName = workName;
}
public String getUniversity() {
return university;
}
public void setUniversity(String university) {
this.university = university;
}
public String getEventCate() {
return eventCate;
}
public void setEventCate(String eventCate) {
this.eventCate = eventCate;
}
public String getParticipant() {
return participant;
}
public void setParticipant(String participant) {
this.participant = participant;
}
public String getTeacher() {
return teacher;
}
public void setTeacher(String teacher) {
this.teacher = teacher;
}
public void dataManaged(int teamID, String workName, String university,String eventCate,String student, String teacher) {
this.teamID = teamID;
this.workName = workName;
this.university = university;
this.participant=student;
this.teacher=teacher;
this.eventCate = eventCate;
}
public void printTeam(){
System.out.println("参赛队编号:"+teamID);
System.out.println("参赛作品名称:"+workName);
System.out.println("参赛学校:"+university);
System.out.println("参赛类别:"+eventCate);
System.out.println("参赛者:"+participant);
System.out.println("指导老师:"+teacher);
}
}
//Node.java
public class Node {
static int times = 0;
int teamID;
Team team;
Node left;
Node right;
public Node() {
}
public Node(int teamID) {
this.teamID = teamID;
}
public Node(int id,Team team){
this.teamID = id;
this.team = team;
}
//查找要删除的结点
public void setTeamName(int id){
this.teamID = id;
}
public int getTeamID(){
return this.teamID;
}
public Team getTeam() {
return team;
}
public void setTeam(Team team) {
this.team = team;
}
public int getTimes(){
return times;
}
public Node search(int teamID) {
if(teamID == this.teamID) { //找到直接返回
return this;
} else if(teamID < this.teamID) { //如果查找的结点的值小于当前结点,向左子树递归查询
if(this.left == null) {
//如果左子节点为空则返回null
return null;
}
times++;
return this.left.search(teamID);
} else {
if(this.right == null) {
return null;
}
times++;
return this.right.search(teamID);
}
}
//查找要删除结点的父节点
public Node searchParent(int teamID) {
//如果当前结点就是要查找结点的父节点,就返回
if((this.left != null && this.left.teamID == teamID) ||
(this.right != null && this.right.teamID == teamID)) {
return this;
} else if(teamID < this.teamID && this.left != null) { //如果要查找的结点的值小于当前节点的值且当前结点的左子节点不为空
return this.left.searchParent(teamID); //向左子树递归查找
} else if(teamID > this.teamID && this.right != null) {
return this.right.searchParent(teamID);
} else {
return null; //没有找到父节点
}
}
//添加节点[递归的方式添加结点,要满足二叉排序树的规则]
public void add(Node node) {
if(node == null) {
return;
}
//判断传入结点的值和当前子树的根节点的值的关系
if(node.teamID < this.teamID) {
//如果当前结点的左子节点为null,则将node添加到当前结点的左子节点上
if(this.left == null) {
this.left = node;
} else { //递归向左子树添加
this.left.add(node);
}
} else { //传入结点的值大于等于当前子树的根节点的值
if(this.right == null) {
this.right = node;
} else { //递归向右子树添加
this.right.add(node);
}
}
}
//中序遍历该BST树
public void infixOrder() {
if(this.left != null) {
this.left.infixOrder();
}
System.out.println(this);
if(this.right != null) {
this.right.infixOrder();
}
}
//前序遍历该树
public void preOrder() {
System.out.println(this);
if(this.left != null) {
this.left.preOrder();
}
if(this.right != null) {
this.right.preOrder();
}
}
public boolean contains(int teamID) {
if(this.teamID == teamID) {
return true;
} else if(teamID < this.teamID) {
if(this.left ==null) {
return false;
} else {
return this.left.contains(teamID);
}
} else {
if(this.right == null) {
return false;
} else {
return this.right.contains(teamID);
}
}
}
@Override
public String toString() {
return "Node{" +
"teamID = " + teamID +
'}';
}
public int BST_ASL(Node node, int weight)
{
weight++;
int a = weight;
if (node.left!=null)
weight += BST_ASL(node.left, a);
if (node.right!=null)
weight += BST_ASL(node.right, a);
return weight;
}
}
//BinarySortTree.java
import java.util.ArrayList;
public class BinarySortTree {
Node root;
public Node getRoot() {
return root;
}
//查找给定节点是否在该二叉树排序上
public boolean contains(int value) {
if(root == null) {
return false;
} else {
return root.contains(value);
}
}
//查找结点
public Node search(int value) {
if(root == null) {
return null;
} else {
return root.search(value);
}
}
//查找父节点
public Node searchParent(int value) {
if(root == null) {
return null;
} else {
return root.searchParent(value);
}
}
//删除节点
public void delNode(int value) {
if(root == null) {
return;
} else { //找到要删除的节点
Node targetNode = search(value);
//如果没有找到要删除的节点就返回
if(targetNode == null) {
return;
}
//如果这个二叉排序树只有一个节点
if(root.left == null && root.right == null) {
root = null;
return;
}
//去找要删除节点的父节点
Node parent = searchParent(value);
//判断删除的是叶子节点
if(targetNode.left == null && targetNode.right == null) {
//判断删除的节点是父节点的左子节点还是右子节点
if(parent.left != null && parent.left.teamID == value) { //是左子节点
parent.left = null;
} else if(parent.right != null && parent.right.teamID == value) {
parent.right = null;
}
} else if(targetNode.left != null && targetNode.right != null) { //删除有两颗子树的节点
//从targetNode的右子树找到最小的节点,用临时变量保存该最小节点的值并删除,最后再吧临时变量的值赋给该节点的值
int minValue = delRightMin(targetNode.right);
targetNode.teamID = minValue;
} else { //删除只有一颗子树的节点
if(targetNode.left != null) { //若删除的节点有左子节点
//这里需要判断下父亲节点是否为空,如果不判断会出现空指针异常。因为如果二叉排序树只有根节点和它的一个左子节点,
// 刚好要删除这个根节点,由于它没有父亲节点,所以返回的是null
if(parent != null) {
if(parent.left.teamID == value) { //targetNode是parent的左子节点
parent.left = targetNode.left;
} else { ///targetNode是parent的右子节点
parent.right = targetNode.left;
}
} else {
root = targetNode.left;
}
} else { //删除的节点有右子节点
if(parent != null) { //同理也需要判段父亲节点是否为空
if(parent.left.teamID == value) { //targetNode是parent的左子节点
parent.left = targetNode.right;
} else {
parent.right = targetNode.right;
}
} else {
root = targetNode.right;
}
}
}
}
}
public int delRightMin(Node node) {
Node target = node;
while(target.left != null) {
target = target.left;
}
//退出while循环时,target就指向了最小节点;
delNode(target.teamID);
return target.teamID;
}
public int delLeftMax (Node node) {
Node target = node;
while(target.right != null) {
target = target.right;
}
//退出while循环时,target就指向了最大节点
delNode(target.teamID);
return target.teamID;
}
//中序遍历BST
public void infixOrder() {
if(root != null) {
root.infixOrder();
} else {
System.out.println("空树,无法遍历");
}
}
//前序遍历该树
public void preOrder() {
if(root != null) {
root.preOrder();
} else {
System.out.println("null");
}
}
//添加节点
public void add(Node node) {
if(root == null) {
root = node; //如果root为空,令其直接指向node
} else {
root.add(node);
}
}
}
//VertexType.java
public class VertexType { 顶点结构
int placeID; //定点序号
String placeName; //顶点名字
public VertexType(int placeID,String placeName)
{
this.placeID = placeID;
this.placeName = placeName;
}
public int getPlaceID() {
return placeID;
}
public void setPlaceID(int placeID) {
this.placeID = placeID;
}
public String getPlaceName() {
return placeName;
}
public void setPlaceName(String placeName) {
this.placeName = placeName;
}
}
//Graph.java
public class Graph {
//根据图 得出的已知默认条件
static int ver = 10;
static int edg = 13;
static String[] places = {"北门","北苑食堂","计算机学院","文理大楼","图书馆",
"住宿区","笃学楼","东苑食堂","体育中心","南门"};
static int nullPoint = 1024;
int vertexNum,edgings; //图的 点数 、 边数
VertexType[] vertexTypes = new VertexType[10]; //定义10个顶点
int [][] distance = new int[10][10]; //用来存取权值 (地图上两个地点之间的距离)
public Graph() //改进方式 将数据填写在文档中进行读取
{
vertexNum = ver;
edgings = edg;
for (int i=0;i<vertexNum;i++){ //地点名称存储
vertexTypes[i] = new VertexType(i+1,places[i]);
}
//地点的地理位置距离 存储
distance[0][1] = 100; //北门
distance[0][2] = 200;
distance[1][0] = 100; //北苑食堂
distance[1][4] = 400;
distance[2][0] = 200; //计算机学院
distance[2][3] = 100;
distance[3][2] = 100; //文理大楼
distance[3][5] = 550;
distance[3][6] = 300;
distance[4][1] = 400; //图书馆
distance[4][6] = 150;
distance[4][7] = 100;
distance[5][3] = 550; //住宿区
distance[5][8] = 450;
distance[6][3] = 300; //笃学楼
distance[6][4] = 150;
distance[6][8] = 500;
distance[6][9] = 200;
distance[7][4] = 100; //东苑食堂
distance[7][9] = 600;
distance[8][5] = 450; //体育中心
distance[8][6] = 200;
distance[8][9] = 250;
distance[9][6] = 200; //南门
distance[9][7] = 600;
distance[9][8] = 250;
//该二维矩阵为对称矩阵
//无边相连的点 距离设为 nullPoint 1024
for (int i=0;i<vertexNum;i++){
for (int n=0;n< vertexNum;n++){
if (distance[i][n] == 0) {
distance[i][n] = 1024;
}
}
}
}
public void ShortestPath_DIJ(Graph graph,int v0,int v1)
{
//v0 为用户输入的起始地点序号(从1开始) 故使用时 需减1
//v1 为用户输入的终点地点序号(从1开始) 故使用时 需减1
int node = graph.vertexNum; //先把地图的位置顶点数用n来表示
boolean[] isShortest = new boolean[node]; //每执行一次循环找出最小路径,如果找出一个最小路径就把该点置为true
int[] DIJ = new int[node]; //存权值(最小的),如果有更小的将会代替该位置上大的权值
int[] Path = new int[node]; //记录该索引顶点的前驱顶点
//初始化,先找出v0结点的邻居结点的各个权值
for(int i=0; i<node; i++)
{
isShortest[i] = false; //S中先都置为false
DIJ[i] = graph.distance[v0 -1][i]; //最开始先把v0与其他几个顶点的权值赋值给D[i]
if(DIJ[i] >0 && DIJ[i] != 0) //这一行是否有节点 相通
Path[i] = v0 - 1; //第i个顶点的前驱结点就是v0,表示i和v0相邻
else
Path[i] = -1; //表示i顶点和v0顶点没有相邻,如果相邻必有权值不为无穷大
}
isShortest[v0 - 1] = true; //v0与v0之间路径为0,我们不再讨论
int v = - 1;
//对剩下的n-1个顶点循环。
for(int i = 1; i < node; i++)
{
int m = nullPoint;
//找出D中最小的路径
for(int w = 0; w < node; w++)
if(!isShortest[w] && DIJ[w] < m)
{
m = DIJ[w]; //依次比较D中的权值,找出最小的权值
v = w; //把这个最小权值的位置赋值给v(记录下来)
}
if(v != -1) //表示我们找到了D中的最小路径
{
isShortest[v] = true; //我们找到了最小路径,我们把它置为true,以防下次还找到它
for(int w = 0; w < node; w++)
if(!isShortest[w] && DIJ[v] + graph.distance[v][w] < DIJ[w]) //如果原来的路径D[w](0-n)依次和v0到v的权值再加上v到w的权值进行比较
{
DIJ[w] = DIJ[v] + graph.distance[v][w]; //如果小于了,等于说有最优路径,我们把最优路径赋值给D[w]
Path[w] = v; //并把w的前驱置为v
}
}
}
int [] a = new int[node];
for(int i = 0; i < Path.length; i++)
{
a[i] = -1;
if(Path[v1 - 1] == v0 - 1)
{
continue;
}
else
{
a[i] = Path[v1 - 1];
if(Path[v1 - 1] != -1)
Path[v1 - 1] = Path[Path[v1 - 1]];
}
}
if(DIJ[v1 - 1] != nullPoint)
{
System.out.print("路径是:" + graph.vertexTypes[v0 - 1].placeName);
for(int i = a.length - 1; i >= 0; i --)
{
if(a[i] != -1)
{
System.out.print("->" + graph.vertexTypes[a[i]].placeName);
}
}
System.out.print("->" + graph.vertexTypes[v1 - 1].placeName + "\n");
printf("最短距离为:" + DIJ[v1 - 1]);
}
else
printf("对不起!两者不连通!");
}
public static void printf(Object o)
{
System.out.println(o);
}
public void placePrint(){
int i=1;
System.out.println("地点分别为");
for (String place : places) {
System.out.print(i+"." + place);
System.out.println();
i++;
}
}
}
//DataManage.java
import javax.swing.text.StyledEditorKit;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.*;
public class DataManaged {
static int times = 0;
static String filePath = "D://IDEA/Code/ParticipateTeam/team.txt"; //文件路径
static String imagePath = "D://IDEA/Code/ParticipateTeam/map.jpg"; //图片路径
static int eventNum = 31; //赛事类别 目前一共有31个
static String[] eventName = {"大数据实践", "信息图形设计", "动态信息影像(MG动画)", "交互信息设计",
"数据可视化","人工智能实践赛","Web应用与开发","管理信息系统","算法设计与应用",
"移动应用开发","移动应用开发(非游戏类)", "医药卫生","数字生活","运动健身",
"城市管理","行业应用","动画", "纪录片", "数字短片","微电影","新媒体漫画","环境设计",
"平面设计","产品设计", "交互媒体设计","游戏设计","虚拟现实VR与增强现实AR","汉语言文学",
"计算机基础与应用类课程微课", "虚拟实验平台","中、小学数学或自然科学课程微课"};
static public void treeStorage(BinarySortTree bTree,ArrayList<Team> teams){ //将元素存入二叉树
for (Team team : teams) {
Node node = new Node(team.getTeamID(),team);
bTree.add(node);
}
}
static public ArrayList<Team> getTeamData(ArrayList<Team> teams,String content,int type){
ArrayList<Team> teamList = new ArrayList<>();
boolean isFind = false;
for (Team team : teams) {
switch (type){
case 1: //1 为学校名称
if (team.getUniversity().equals(content)){
teamList.add(team);
isFind = true;
}
break;
case 2: //2 为赛事类别
if (team.getEventCate().equals(content)){
teamList.add(team);
isFind = true;
}
break;
default:
System.out.println("ERROR");
break;
}
}
if (isFind == false){
System.out.println("无信息匹配");
return null;
}
return teamList;
}
static public void callSystem(ArrayList<Team> teams){ //叫号系统
System.out.println("匹配到队伍信息");
System.out.println("作品ID\t\t参赛作品\t\t参赛作者\t\t参赛类别");
System.out.println("*******************************************");
System.out.println("请各位选手,按以下顺序进入场第" + 1 + "个决赛室"); //31个赛事类别 9个决赛室
System.out.println("*******************************************");
System.out.println("---------------------------------------");
int cir = 1;
for (int i=0;i<eventName.length;i++) {
if ((i/4+1)!=cir){
cir = i/4+1;
System.out.println("*******************************************");
System.out.println("请各位选手,按以下顺序进入场第" + cir + "个决赛室");
System.out.println("*******************************************");
}
String event = eventName[i];
ArrayList<Team> teamList = getTeamData(teams,event,2);
Queue<Team> q1 = new PriorityQueue<Team>(com);
for (Team team : teamList) {
q1.add(team); //其中按照ID大小 从小到大 按顺序排列
}
System.out.println("");
while (!q1.isEmpty()){
Team team = q1.poll();
System.out.println(team.getTeamID()+"--"+team.getWorkName() + "--"
+ team.getParticipant() + "--" +team.getEventCate());
try {
Thread.sleep(500);
System.out.println("(等待该小组完成)");
} catch (Exception e) {
System.out.println("Error");;
}
System.out.println("");
}
System.out.println("--------------------------------------");
}
}
static Comparator<Team> com = new Comparator<Team>() {
@Override
public int compare(Team o1, Team o2) {
return o1.teamID-o2.teamID; //按照 团队ID 进行排列 升序
}
};
static public double log(double value,double base){
return Math.log(value)/Math.log(base);
}
static public void printMenu_0(){
System.out.println("********************************************************");
System.out.println("* *");
System.out.println("* 赛事管理系统 *");
System.out.println("* 输入序号进行相应操作 *");
System.out.println("* 1.管理队伍信息(添加、修改、删除) *");
System.out.println("* 2.查询队伍信息(根据队伍ID查找) *");
System.out.println("* 3.根据参赛学校查找队伍(有序输出) *");
System.out.println("* 4.决赛室叫号系统(9个决赛室) *");
System.out.println("* 5.校园导航系统管理(两地距离最短路线) *");
System.out.println("* 0.结束系统管理系统(关闭程序) *");
System.out.println("* *");
System.out.println("********************************************************");
}
static public void printMenu_1(){
System.out.println("********************************************************");
System.out.println("* *");
System.out.println("* 管理队伍信息 *");
System.out.println("* 输入序号进行相应操作 *");
System.out.println("* 1.查看队伍信息 *");
System.out.println("* 2.添加队伍信息 *");
System.out.println("* 3.删除队伍信息 *");
System.out.println("* 0.返回上个界面 *");
System.out.println("* *");
System.out.println("********************************************************");
}
static public void printMenu_3(){
System.out.println("********************************************************");
System.out.println("* *");
System.out.println("* 查找队伍信息 *");
System.out.println("* 输入序号进行相应操作 *");
System.out.println("* 1.按照参赛学校查找队伍信息 *");
System.out.println("* 2.按照赛事类别查找队伍信息 *");
System.out.println("* 0.返回上个界面 *");
System.out.println("* *");
System.out.println("********************************************************");
}
public static void main(String[] args) {
ArrayList<Team> teams = new ArrayList<>(); //存储团队信息
BinarySortTree bTree = new BinarySortTree(); //二叉树存储 进行团队的查找
Graph graph = new Graph();
Scanner sc = new Scanner(System.in);
boolean program = true;
FileManage fileManage = new FileManage();
while (program){
teams = fileManage.getTeams();
treeStorage(bTree,teams); //将ArrayList中的元素数据存入二叉树中
printMenu_0();
int order = sc.nextInt();
switch (order){
case 0:
program = false;
break;
case 1:
printMenu_1();
int order_1 = sc.nextInt();
switch (order_1){
case 0:
break;
case 1:
for (Team team : teams) {
System.out.println("-----------------------------------------------");
team.printTeam();
System.out.println("-----------------------------------------------");
}
break;
case 2:
fileManage.setFileWrite(filePath);
break;
case 3:
System.out.println("======================================");
System.out.println("请输入需删除的队伍ID");
int id = sc.nextInt();
fileManage.fileDelete(filePath,id);
System.out.println("======================================");
break;
default:
break;
}
try {
System.out.println("等待中");
System.out.println("======================================");
Thread.sleep(3000);
} catch (InterruptedException e) {
System.out.println("Error");;
}
break;
case 2:
System.out.println("=======================================");
System.out.println(" ID查找队伍 ");
System.out.println("请输入需查找队伍信息的队伍ID(格式:202100XXXX)");
int id = sc.nextInt();
Node node = bTree.search(id);
if (node == null){
System.out.println("查找失败,无相关队伍ID与输入匹配");
} else{
System.out.println("查找到队伍信息:");
node.getTeam().printTeam();
System.out.println("查找次数:"+ node.getTimes());
int n = teams.size();
System.out.print("平均查找次数ASL:");
double asl = bTree.root.BST_ASL(bTree.root,0);
System.out.println(asl/teams.size());
System.out.println();
System.out.println(((n+1)/n) * (log(n,2)) - 1); //查找次数有误
}
try {
System.out.println("等待中");
System.out.println("=======================================");
Thread.sleep(3000);
} catch (InterruptedException e) {
System.out.println("Error");;
}
break;
case 3:
ArrayList<Team> getTeam = new ArrayList<>();
printMenu_3();
int order_2 = sc.nextInt();
String str;
System.out.println("=======================================");
System.out.println(" 查找队伍信息 ");
switch (order_2){
case 0:
break;
case 1:
System.out.println("请输入学校名称:");
str = sc.next();
getTeam = getTeamData(teams,str,1);
if (getTeam.get(0) == null){
System.out.println("查找失败,该学校没有参加比赛");
}else {
System.out.println("以下为查找到的学校参加比赛的队伍信息:");
for (Team team : getTeam) {
System.out.println("================================");
team.printTeam();
System.out.println("================================");
}
}
break;
case 2:
System.out.println("请输入赛事类别:");
str = sc.next();
getTeam = getTeamData(teams,str,2);
if (getTeam.get(0) == null){
System.out.println("查找失败,该学校没有参加比赛");
}else {
System.out.println("以下为查找到的学校参加比赛的队伍信息:");
for (Team team : getTeam) {
System.out.println("---------------------------------");
team.printTeam();
System.out.println("---------------------------------");
}
}
break;
}
try {
System.out.println("等待中");
Thread.sleep(3000);
System.out.println("=======================================");
} catch (InterruptedException e) {
System.out.println("Error");;
}
break;
case 4:
System.out.println("======================================");
System.out.println(" 叫号系统 ");
callSystem(teams);
try {
System.out.println("等待中");
System.out.println("======================================");
Thread.sleep(5000);
} catch (InterruptedException e) {
System.out.println("Error");;
}
break;
case 5:
//输出图片
//fileManage.getImage(imagePath);
int v1;
int v2;
System.out.println("=======================================");
System.out.println("地点如下:");
graph.placePrint();
System.out.println("按照地点序号输入,查找两点最短路径");
System.out.println("请输入起始点:");
v1 = sc.nextInt();
System.out.println("请输入终点:");
v2 = sc.nextInt();
graph.ShortestPath_DIJ(graph,v1,v2);
try {
System.out.println("等待中");
System.out.println("======================================");
Thread.sleep(3000);
} catch (InterruptedException e) {
System.out.println("Error");;
}
break;
}
}
}
}