一、问题定义
1、管理参赛队的基本信息
2、实现基于二叉排序树的查找
3、实现按参赛学校查询参赛团队
4、决赛叫号系统
5、校园导游程序
二、问题分析
管理参赛队的基本信息
队伍数据的存储:需要确定如何存储参赛队的基本信息,例如队伍ID、项目名称、学校名称、参赛类别、参赛成员、指导教师等。可以使用数据结构来存储这些信息,例如二叉搜索树(BST)或哈希表等。
队伍数据的添加和删除:需要实现功能来添加新的参赛队信息和删除已有的参赛队信息。这涉及到插入和删除节点的操作,以及对存储数据结构的更新。
队伍数据的更新:需要提供功能来更新队伍的信息。这可以通过遍历数据结构或利用索引来实现。
使用单链表来存储参赛队的基本信息具有以下优势
-
动态性:单链表的大小可以根据需要进行动态调整。在参赛队伍的管理中,可能需要频繁地添加、删除队伍信息,而单链表可以通过修改节点的链接来实现高效的插入和删除操作,无需重新分配内存空间。
-
灵活性:由于单链表的节点只包含指向下一个节点的链接,可以轻松地在链表中插入、删除或移动节点,而无需对整个链表进行重构。这样可以方便地对参赛队伍的信息进行修改和调整。
-
简单的插入和删除:对于单链表,插入和删除节点的操作只需要修改相邻节点的链接,不需要移动其他节点,因此插入和删除的时间复杂度为O(1)。这使得在管理参赛队伍时可以高效地进行插入和删除操作。
-
遍历效率高:可以从单链表的头节点开始,通过顺序访问每个节点,高效地遍历所有参赛队伍的信息。这对于列出所有队伍或进行查询操作非常方便。
-
节省内存空间:相比于数组等静态数据结构,单链表在存储参赛队伍信息时可以更加灵活地利用内存空间。每个节点只包含所需的数据和一个指向下一个节点的链接,而不需要预先分配固定大小的内存块。
实现基于二叉排序树的查找
实现基于二叉排序树的查找。根据提示输入参赛队编号,若查找成功,输出该赛事类别对应的基本信息(参赛作品名称、参赛学校、赛事类别、参赛者和指导老师信息),同时,输出查找成功时的平均查找长度ASL;否则,输出“查找失败!”。请输出ASL(成功)的计算表达式和结果值。
实现按参赛学校查询参赛团队
对于按参赛学校查询参赛团队并且要求有序输出的情况,可以选择选择排序
选择排序的优势在于其简单性和易于实现。以下是选择排序的一些优势:
-
简单易懂:选择排序是一种直观且易于理解的排序算法。它的基本思想是通过每次选择未排序部分的最小(或最大)元素并放置在已排序部分的末尾,以逐步构建有序序列。
-
不占用额外空间:选择排序是一种原地排序算法,即不需要额外的辅助数组或数据结构来进行排序。它仅需要在原始数组中进行元素的交换操作,节省了空间复杂度。
-
最好情况下的性能优势:选择排序在最好情况下的比较次数是确定的。无论输入数据的顺序如何,都需要进行 n-1 次比较操作,其中 n 是待排序元素的个数。这使得选择排序在某些特定场景下(如已经基本有序的序列)可能比其他排序算法更加高效。
-
对于小规模数据或部分有序数据的适应性:由于选择排序的特点,它在处理小规模数据或者部分有序的数据时可能表现良好。因为选择排序的比较次数是固定的,不受数据规模的影响,并且对已经有序的部分不会进行多余的比较操作。
决赛叫号系统
首先是遍历所有的队伍节点,然后按赛事类别把分开,之后使用选择排序把把队伍按编号排序,最后建立九个进程模拟叫号过程
校园导游程序
这显然是一个图论问题,而且校园内道路一般是双向通行的,所以这是一个无向图。对 于图的存储结构而言,图中各个景点的存储结构有邻接表和邻接矩阵两种存储结构,考虑到 顶点个数少于 50 个,所以邻接表和邻接矩阵的复杂度相同。本题中选择使用邻接矩阵来表 示图。 任务中要求求解出图中景点的问路查询,即为给定两个源点,求解出两个顶点之间的最 短路径。根据数据结构课程所学知识,有多种经典算法可以解决最短路径问题,包括 Dijkstra 算法,Floyd-Warshell 算法,Bellman-Ford 算法和深度优先遍历。不同是算法有不同的算法复 杂度,考虑到校园中道路没有负权边,即算法均可解决最短路径问题。 其中 Dijkstra 算法求的是单源最短路径:即从一个结点出发到其它所有结点的最短路径, 算法的时间复杂度为 O(n 2 ),但题目要求任意两个结点的最短路径,所以还是要在外层增加 一个循环,以求得多源最短路径。 而 Floyd 算法求的是多源最短路径:即从任意结点出发到其它所有结点的最短路径,算 法的时间复杂度为 O(n 3 ),它可以一次性求得所有结点间的最短路径,且算法思想简单,便 于理解。所以我们这一项目采用 dijkstra
三、概要设计
1、管理参赛队的基本信息
读写工具类 FileIO
,其中包含了两个静态方法:readTeamsFromFile()
和 writeTeamsToFile()
。
-
readTeamsFromFile()
方法用于从文件中读取参赛团队信息并创建团队链表。它接收一个文件名作为参数,并返回一个团队链表对象。- 首先,通过创建
BufferedReader
对象和使用FileReader
从文件中读取数据。 - 然后,使用
readLine()
方法逐行读取文件内容,直到读取到末尾(返回值为null
)。 - 对于每一行数据,使用
\t#\t
作为分隔符将数据拆分为不同的字段。 - 将拆分后的字段转换为相应的类型,并使用它们创建一个新的
Team
对象。 - 将新创建的团队对象添加到团队链表中。
- 最后,返回完整的团队链表对象。
- 首先,通过创建
-
writeTeamsToFile()
方法用于将团队链表中的参赛团队信息写入文件。它接收一个团队链表对象和一个文件名作为参数。- 首先,通过创建
BufferedWriter
对象和使用FileWriter
打开文件以进行写操作。 - 使用团队链表的
getHead()
方法获取链表的头节点。 - 循环遍历团队链表,直到链表结束。
- 对于每个团队,使用
write()
方法将团队的各个字段以\t#\t
作为分隔符写入文件。 - 在每行数据的末尾添加换行符
\n
。 - 最后,关闭文件写入流。
- 首先,通过创建
Team
类包含了私有字段 teamNumber
、projectName
、school
、eventCategory
、participants
和 guideTeacher
,分别表示团队的编号、项目名称、学校、赛事类别、参与者和指导教师。
这个 Team
类可以在团队链表的实现中使用,每个 Team
对象表示一个团队的信息,通过 next
字段连接在一起形成一个链表。链表的头节点可以通过调用 getHead()
方法获取。可以使用这个类来创建、修改和访问团队的属性,并在链表中进行操作。
定义了一个 TeamLinkedList
类,表示团队链表。它包含了管理团队链表的操作方法,例如添加团队、删除团队、更新团队信息等。
以下是这段代码的思路:
TeamLinkedList
类包含了一个私有字段head
,表示链表的头部。- 类中的构造函数用于初始化链表,将头部设置为
null
。 getHead()
方法用于获取链表的头部。addTeam()
方法用于从用户输入中添加一个新的团队。它会提示用户输入团队的各个属性,然后创建一个新的Team
对象,并调用addTeam(Team newTeam)
方法将其添加到链表末尾。addTeam(Team newTeam)
方法用于将给定的团队对象添加到链表末尾。如果链表为空,则将该团队对象作为头部;否则,遍历链表直到末尾,然后将新团队对象链接到链表的末尾。deleteTeam()
方法用于从用户输入中删除一个团队。它会提示用户输入要删除的团队编号,然后调用deleteTeam(int teamNumber)
方法进行删除。deleteTeam(int teamNumber)
方法用于根据给定的团队编号从链表中删除团队。它会遍历链表,找到匹配的团队编号并删除对应的团队节点。updateTeam()
方法用于从用户输入中更新一个团队的信息。它会提示用户输入要更新的团队编号,然后根据用户输入的信息创建一个新的Team
对象,并调用updateTeam(int teamNumber, Team updatedTeam)
方法进行更新。updateTeam(int teamNumber, Team updatedTeam)
方法用于根据给定的团队编号找到相应的团队节点,并更新其属性值为新的团队对象的属性值。isTeamNumberExists(int teamNumber)
方法用于检查给定的团队编号是否已存在于链表中。findTeamById()
方法用于从用户输入中查找一个团队的信息。它会提示用户输入要查找的团队编号,并从文件中读取团队信息,然后使用二叉排序树进行查找操作。searchBySchool(String school)
方法用于根据给定的学校名称在链表中搜索匹配的团队。它会遍历链表,找到学校名称与给定名称相匹配的团队,并将其存储在一个列表中。
2、实现基于二叉排序树的查找
定义了一个 BinarySearchTree
类,表示二叉排序树。它用于存储参赛队伍的信息,并支持插入和查找操作。
以下是这段代码的思路:
BinarySearchTree
类中定义了一个内部静态类Node
,表示二叉排序树的节点。每个节点包含一个参赛队编号和对应的参赛团队信息,以及左子节点和右子节点的引用。- 类中定义了
root
字段,表示二叉排序树的根节点。还有nodeCount
字段,表示二叉排序树中节点的数量,以及searchCount
字段,表示查找操作的次数。 - 类中的构造函数初始化根节点为
null
,节点数量和查找次数为 0。 insert(int teamNumber, Team team)
方法用于向二叉排序树中插入节点。它接受参赛队编号和参赛团队信息作为参数。在递归方式下,根据节点的大小关系选择插入到左子树或右子树,并更新节点数量。insertNode(Node node, int teamNumber, Team team)
是一个辅助方法,用于在指定的节点下插入新的节点。如果节点为空,则创建一个新节点并返回;否则,根据参赛队编号的大小关系递归调用插入方法,更新左子节点或右子节点的引用。search(int teamNumber)
方法用于在二叉排序树中查找节点并输出基本信息。它接受参赛队编号作为参数。首先调用searchNode(Node node, int teamNumber)
方法来查找节点,如果找到了匹配的节点,就输出参赛团队的信息和查找次数。searchNode(Node node, int teamNumber)
是一个辅助方法,用于在指定的节点下查找匹配的节点。在每次递归中,根据参赛队编号与节点的大小关系进行比较,选择左子节点或右子节点进行下一轮查找。同时,更新查找次数。- 在成功查找到节点后,输出参赛团队的基本信息,并计算平均查找长度 ASL(Average Search Length)作为评估指标。
3、实现按参赛学校查询参赛团队
定义了一个 TeamSearchBySchool
类,用于按照参赛学校查询参赛团队并输出结果。
以下是这段代码的思路:
TeamSearchBySchool
类包含一个私有字段teamList
,表示要进行搜索的团队链表。- 类中的构造函数接受一个
TeamLinkedList
对象作为参数,并将其赋值给teamList
字段。 searchBySchool()
方法用于按照参赛学校查询参赛团队,并输出结果。它会提示用户输入参赛学校的名称,然后调用teamList
的searchBySchool(String school)
方法获取匹配的团队数组。- 如果找到了匹配的团队,就会使用选择排序算法对团队数组按参赛队编号进行排序,并逐个输出团队的信息。
- 如果没有找到匹配的团队,则会输出相应的提示信息。
selectionSort(Team[] teams)
方法使用选择排序算法对团队数组按参赛队编号进行排序。它会遍历团队数组,找到最小的参赛队编号,并将其与当前位置的团队进行交换,以达到排序的目的。swap(Team[] teams, int i, int j)
方法用于交换团队数组中的两个元素。它接受一个团队数组以及两个元素的索引作为参数,并通过临时变量实现两个元素的交换。
4、决赛叫号系统
-
FinalCallSystem
类中定义了私有的决赛室列表finalRoom1
到finalRoom9
,每个列表对应不同的决赛室。 -
在
assignTeams()
方法中,通过遍历参赛队伍列表,将队伍根据赛事类别分配到对应的决赛室列表中。 -
在
announceTeams()
方法中,按照决赛室的顺序,依次输出每个决赛室中的队伍信息。 -
simulateFinals()
方法用于模拟决赛过程。它先调用assignTeams()
方法进行队伍分配,然后创建一个线程列表,每个决赛室对应一个线程。每个线程调用simulateFinalRoom()
方法进行决赛模拟。 -
simulateFinalRoom()
方法接收一个决赛室列表和决赛室名称作为参数,在该决赛室中模拟比赛过程。遍历决赛室中的队伍,输出当前比赛队伍所在的决赛室、队伍编号、参赛作品名称,并模拟比赛过程(这里用Thread.sleep()
模拟比赛时间)。
5、校园导游程序
- 首先定义了一个
Vertex
类,表示图中的顶点。每个顶点包含一个顶点编号、顶点名称和顶点简介。 - 接下来定义了一个
Graph
类,表示图结构。其中包含顶点数量、顶点数组和邻接矩阵。邻接矩阵用于存储顶点之间的边和权重信息。 Graph
类的构造函数接受顶点数量作为参数,初始化顶点数组和邻接矩阵。初始时,将邻接矩阵中的所有元素设置为最大值表示无穷大的权重。Graph
类提供了添加顶点和添加边的方法。addVertex(Vertex vertex)
方法用于向图中添加顶点,将顶点对象存储在对应位置的顶点数组中。addEdge(int source, int destination, int weight)
方法用于添加边,将边的权重存储在邻接矩阵中对应的位置上。Graph
类还提供了获取顶点信息的方法getVertex(int vertexId)
,根据顶点编号返回对应的顶点对象。- 接下来定义了一个
ShortestPath
类,用于执行最短路径算法。它包含一个Graph
对象作为成员变量,用于进行最短路径计算。 ShortestPath
类的构造函数接受一个Graph
对象作为参数,并将其保存到成员变量中。ShortestPath
类实现了 Dijkstra 算法来计算最短路径。dijkstra(int source, int destination)
方法接受起始顶点和目标顶点作为参数。在该方法中,使用数组distance
记录起始顶点到各顶点的最短距离,使用数组previous
记录最短路径上每个顶点的前驱顶点,使用数组visited
记录顶点是否已被访问。通过迭代过程,逐步更新最短距离和前驱顶点,直到找到最短路径。printShortestPath(int[] previous, int destination)
方法是一个递归方法,用于打印最短路径。通过递归调用,从目标顶点的前驱顶点开始回溯,直到达到起始顶点,打印路径上的顶点名称。- 在
dijkstra(int source, int destination)
方法中,最后将最短路径和最短路径距离组合成一个字符串inf
,并返回该字符串作为最终的结果。