1、问题定义
设计一款赛事管理系统,实现赛务相关的数据管理及信息服务
(1)能够管理各参赛队的基本信息
(2)根据提示输入参赛队编号,若查找成功,输出该赛事类别对应的基本信息
(3)根据赛事类别查询参赛团队,若查找成功,输出该学校参赛的(该赛事类别的)所有团队的基本信息,输出的参赛团队按赛事类别有序输出。
(4)设计叫号系统,所有参赛队按赛事组织文件中的赛事类别分到9个决赛室,决赛室按顺序叫号(5)为参赛者提供各种路径导航的查询服务。
2、问题分析
(1)根据线性表的增、删、改、查对各参赛对的信息进行管理,对于线性表的删除,在初始化时,我们首先初始化线性表的大小和长度并以此大小申请内存数组空间,删除时,直接释放对应的数组空间以及Vector结构体空间。
(2)从team.txt中读取参赛队伍的基本信息,实现基于二叉排序树的查找。二叉排序树又称为二叉查找树,它是一种特殊的二叉树。二叉排序树是一颗空树或者是具有一下性质的二叉树。
(3)对输出的参赛团队按赛事类别有序输出,能够选择多种排序方式,插入排序时间复杂度:最坏情况下为O(N*N),此时待排序列为逆序,或者说接近逆序。最好情况下为O(N),此时待排序列为升序,或者说接近升序,空间复杂度:O(1)。希尔排序先将待排序列进行预排序,使待排序列接近有序,然后再对该序列进行一次插入排序,此时插入排序的时间复杂度为O(N),其时间复杂度平均:O(N^1.3)空间复杂度:O(1)。选择排序每次从待排序列中选出一个最小值,然后放在序列的起始位置,直到全部待排数据排完即可。实际上,我们可以一趟选出两个值,一个最大值一个最小值,然后将其放在序列开头和末尾,这样可以使选择排序的效率快一倍。冒泡排序则是左边大于右边交换一趟排下来最大的在右边。
(4)要求模拟决赛叫号系统,演示省赛现场各决赛室的参赛队进场情况,可以运用栈和队列的知识。采用顺序存储的栈称为顺序栈,它利用一组地址连续的存储单元存放自栈底到栈顶的数据元素,同时附设一个指针(top)指示当前栈顶元素的位置。若存储栈的长度为StackSize,则栈顶位置top必须小于StackSize。当栈存在一个元素时,top等于0,因此通常把空栈的判断条件定位top等于-1。采用链式存储的栈称为链栈,链栈的优点是便于多个栈共享存储空间和提高其效率,且不存在栈满上溢的情况。通常采用单链表实现,并规定所有操作都是在单链表的表头进行的。这里规定链栈没有头节点,Lhead指向栈顶元素。
(5)任务中要求求解出图中景点的问路查询,即为给定两个源点,求解出两个顶点之间的最短路径。根据数据结构课程所学知识,有多种经典算法可以解决最短路径问题,包括 Dijkstra算法,Floyd-Warshell 算法,Bellman-Ford 算法和深度优先遍历。不同是算法有不同的算法复杂度,考虑到校园中道路没有负权边,即算法均可解决最短路径问题。
其中 Dijkstra 算法求的是单源最短路径:即从一个结点出发到其它所有结点的最短路径,算法的时间复杂度为 O(n 2 ),但题目要求任意两个结点的最短路径,所以还是要在外层增加一个循环,以求得多源最短路径。
3、概要设计
(1)根据线性表的增、删、改、查对各参赛对的信息进行管理,对于线性表的删除,在初始化时,我们首先初始化线性表的大小和长度并以此大小申请内存数组空间,删除时,直接释放对应的数组空间以及Vector结构体空间。
//插入数据
public void insert(int i, T x) {
if (x == null)
return;
// 若数组满,则扩充顺序表容量
if (this.len == element.length) {
// temp也引用elements数组
Object[] temp = this.element;
// 重新申请一个容量更大的数组
this.element = new Object[temp.length * 2];
// 复制数组元素,O(n)
for (int j = 0; j < temp.length; j++) {
this.element[j] = temp[j];
}
}
// 下标容错
if (i < 0)
i = 0;
if (i > this.len)
i = this.len;
// 元素后移,平均移动len/2
for (int j = this.len - 1; j >= i; j--) {
this.element[j + 1] = this.element[j];
}
this.element[i] = x;
this.len++;
}
//在顺序表最后插入x元素
public void append(T x) {
insert(this.len, x);
}
//删除
public T remove(int i) {
if (this.len == 0 || i < 0 || i >= this.len) {
return null;
}
T old = (T) this.element[i];
// 元素前移,平均移动len/2
for (int j = i; j < this.len - 1; j++) {
this.element[j] = this.element[j + 1];
}
this.element[this.len - 1] = null;
this.len--;
return old;
}
// 删除线性表所有元素
public void removeAll() {
this.len = 0;
}
(2)实现基于二叉排序树的查找。
typedef struct BSTNode{
int key;
struct BSTNode *lchild, *rchild;
}BSTNode, *BSTree;
//在二叉排序树中查找值为key的结点
BSTNode *BST_Search(BSTree_T, int key){
while(T!=NULL && key!=T->key){ //若树空或等于根结点值,则结束循环
if(key < T->key)
T = T->lchild; //小于,则在左子树上查找
else
T = T->rchild; //大于,则在右子树上查找
}
return T;
}
(3)实现对对输出的参赛团队按赛事类别有序输出,能够选择多种排序方式,
public class demo_sort {
public static void main(String[] args) {
//冒泡排序算法
int[] numbers=new int[]{1,5,8,2,3,9,4};
//需进行length-1次冒泡
for(int i=0;i<numbers.length-1;i++)
{
for(int j=0;j<numbers.length-1-i;j++)
{
if(numbers[j]>numbers[j+1])
{
int temp=numbers[j];
numbers[j]=numbers[j+1];
numbers[j+1]=temp;
}
}
}
System.out.println("从小到大排序后的结果是:");
for(int i=0;i<numbers.length;i++)
System.out.print(numbers[i]+" ");
}
}
(4)要求模拟决赛叫号系统,演示省赛现场各决赛室的参赛队进场情况,可以运用栈和队列的知识。
//顺序队列 头文件 SeqQueue.h
#ifndef _SEQQUEUE_H_
#define _SEQQUEUE_H_
typedef void SeqQueue;
SeqQueue *SeqQueue_Create(int capacity);
void SeqQueue_Destroy(SeqQueue *queue);
void SeqQueue_Clear(SeqQueue *queue);
int SeqQueue_Append(SeqQueue *queue, void *item); //加入队列
void *SeqQueue_Retrieve(SeqQueue *queue); //出队列
void *SeqQueue_Header(SeqQueue *queue); //获取头部
int SeqQueue_Length(SeqQueue *queue);
int SeqQueue_Capacity(SeqQueue *queue);
#endif
//顺序队列 源文件 SeqQueue.c
#include "SeqList.h"
#include "SeqQueue.h"
SeqQueue *SeqQueue_Create(int capacity) // O(1)
{
return SeqList_Create(capacity);
}
void SeqQueue_Destroy(SeqQueue *queue) // O(1)
{
SeqList_Destroy(queue);
}
void SeqQueue_Clear(SeqQueue *queue) // O(1)
{
SeqList_Clear(queue);
}
int SeqQueue_Append(SeqQueue *queue, void *item) // O(1)加入队列
{
return SeqList_Insert(queue, item, SeqList_Length(queue));
}
void *SeqQueue_Retrieve(SeqQueue *queue) // O(n) 出队列
{
return SeqList_Delete(queue, 0);
}
void *SeqQueue_Header(SeqQueue *queue) // O(1) 获取头部元素
{
return SeqList_Get(queue, 0);
}
int SeqQueue_Length(SeqQueue *queue) // O(1)
{
return SeqList_Length(queue);
}
int SeqQueue_Capacity(SeqQueue *queue) // O(1)
{
return SeqList_Capacity(queue);
}
(5)不同是算法有不同的算法复杂度,考虑到校园中道路没有负权边,即算法均可解决最短路径问题。
结点信息的数据结构:类——View 用于存储各个结点的详细信息,如景点编号、名称
和景点介绍,该类还提供了相应的 setter 和 getter。
public class View {
//景点的编号
private int id;//景点的名称
private String name;
//景点的介绍
private String introduction;
//构造方法
public View(int id, String name, String introduction) {
this.id = id;
this.name = name;
this.introduction = introduction;
}
}
下面是图的数据结构:前面提到过本题采用邻接矩阵来存储图,并且是无向图的存储格
式。提供了图结构最大结点数为 20:即最多扩展 20 个结点,原图选取了 12 个结点作为校
内景点。顶点数组采用了 View 类型的数组以便于信息的查询,因为要使用到 Floyd 算法,
所以还有图的前趋结点数组(路径数组),另外该类还提供了相应的 getter 和 setter 并在构造
方法中初始化了图的各种信息。
public class MGraph {
//最大顶点数
public static final int MAX_SIZE = 20;
//最大权值,即∞
public static final int MAX_INT = 65535;
//顶点数组
private View[] vertex = new View[MAX_SIZE];
//图的邻接矩阵
private int[][] adj = new int[MAX_SIZE][MAX_SIZE];
//图的前趋结点数组
private int[][] pre = new int[MAX_SIZE][MAX_SIZE];
//图的顶点数和边数
private int vertexNum, edgeNum;
}