数据结构预习报告

1.问题定义

本次课程设计要求协助中国大学生计算机设计大赛江苏省组委会,设计一款赛事管理系统,实现赛务相关的数据管理及信息服务,该系统能够为省级赛事管理解决以下问题:

(1)能够管理各参赛队的基本信息(包含参赛队编号,参赛作品名称,参赛学校,赛事类别,参赛者,指导老师),赛事类别共11项(参见大赛官网jsjds.blcu.edu.cn);包括增加、删除、修改参赛队伍的信息。

(2)从team.txt中读取参赛队伍的基本信息,实现基于二叉排序树的查找。根据提示输入参赛队编号,若查找成功,输出该赛事类别对应的基本信息(参赛作品名称、参赛学校、赛事类别、参赛者和指导老师信息),同时,输出查找成功时的平均查找长度ASL;否则,输出“查找失败!”。
(3)能够提供按参赛学校查询参赛团队(或根据赛事类别查询参赛团队),即,根据提示输入参赛学校名称(赛事类别),若查找成功,输出该学校参赛的(该赛事类别的)所有团队的基本信息,输出的参赛团队按赛事类别有序输出。(排序算法可从选择排序、插入排序、希尔排序、归并排序、堆排序中任意选择,并为选择算法的原因做出说明。)

(4)为省赛现场设计一个决赛叫号系统。所有参赛队按赛事组织文件中的赛事类别分到9个决赛室,决赛室按顺序叫号,被叫号参赛队进场,比赛结束后,下一参赛队才能进赛场。请模拟决赛叫号系统,演示省赛现场各决赛室的参赛队进场情况。(模拟时,要能直观展示叫号顺序与进场秩序一致)

(5)赛事系统为参赛者提供赛地的校园导游程序,为参赛者提供各种路径导航的查询服务。以我校长山校区提供比赛场地为例,(请为参赛者提供不少于10个目标地的导航。可为参赛者提供校园地图中任意目标地(建筑物)相关信息的查询;提供任意两个目标地(建筑物)的导航查询,即查询任意两个目的地(建筑物)之间的一条最短路径。

2.问题分析

赛事管理系统的实现需要涉及到多个方面,包括数据结构设计、搜索和排序算法的应用、用户交互和可视化等方面的问题。

在学习过程中,需要熟练掌握二叉排序树的基本原理和实现,选择合适的排序算法对查询结果进行排序,同时需要使用JavaFX等工具对系统界面进行设计。

这显然是一个图论问题,而且校园内道路一般是双向通行的,所以这是一个无向图。对 于图的存储结构而言,图中各个景点的存储结构有邻接表和邻接矩阵两种存储结构,考虑到 顶点个数少于 50 个,所以邻接表和邻接矩阵的复杂度相同。本题中选择使用邻接矩阵来表 示图。

任务中要求求解出图中景点的问路查询,即为给定两个源点,求解出两个顶点之间的最 短路径。根据数据结构课程所学知识,有多种经典算法可以解决最短路径问题,包括 Dijkstra 算法,Floyd-Warshell 算法,Bellman-Ford 算法和深度优先遍历。不同是算法有不同的算法复 杂度,考虑到校园中道路没有负权边,即算法均可解决最短路径问题。

其中 Dijkstra 算法求的是单源最短路径:即从一个结点出发到其它所有结点的最短路径, 算法的时间复杂度为 O(n 2 ),但题目要求任意两个结点的最短路径,所以还是要在外层增加 一个循环,以求得多源最短路径。

而 Floyd 算法求的是多源最短路径:即从任意结点出发到其它所有结点的最短路径,算 法的时间复杂度为 O(n 3 ),它可以一次性求得所有结点间的最短路径,且算法思想简单,便 于理解。所以我们这一项目采用 Floyd 算法来求解最短路径

系统主要包括以下几个组成部分:

数据结构设计:采用二叉排序树进行参赛队伍的基本信息存储,使用链式前向星存储导航图信息和路径信息;

界面设计:使用JavaFX等工具进行设计,包括各种输入框、下拉框,以及部分交互式地图展示等

二叉排序树实现参赛队伍的查找:使用二叉排序树实现快速查找,同时计算ASL;

排序算法实现学校(或赛事类别)查询:采用高效的排序算法对查询结果进行排序;

决赛叫号系统:采用队列模拟决赛叫号程序,实现按赛事类别叫号和模拟参赛队进场;

赛地校园导游:将校园地图导入系统,同时实现不少于10个目标地的导航。采用Floyd算法求解最短路径,同时提供地点信息和路径导航查询。
 

3.概要设计

一.参赛队伍管理模块:

  1. 提供添加参赛队伍信息的功能,包括参赛队编号、参赛作品名称、参赛学校、赛事类别、参赛者和指导老师。提供删除参赛队伍信息的功能,根据参赛队编号删除相应的队伍信息。提供修改参赛队伍信息的功能,根据参赛队编号选择要修改的队伍信息进行更新。
  2. // 参赛队伍信息的结构体定义
    struct team {
        int team_id;              // 参赛队编号
        string team_name;         // 参赛作品名称
        string school_name;       // 参赛学校
        int competition_type;     // 赛事类别代码
        string participants;      // 参赛者
        string instructor;        // 指导老师
        struct team* next;        // 指向下一个结点的指针
    };
     
    // 创建一个新结点并初始化它
    struct team* create_new_node(int team_id, string team_name, string school_name, int competition_type, string participants, string instructor) {
        struct team* new_node = new team;
        new_node->team_id = team_id;
        new_node->team_name = team_name;
        new_node->school_name = school_name;
        new_node->competition_type = competition_type;
        new_node->participants = participants;
        new_node->instructor = instructor;
        new_node->next = NULL;
        return new_node;
    }
     
    // 添加一个新结点到链表中
    void add_team(struct team** head, int team_id, string team_name, string school_name, int competition_type, string participants, string instructor) {
        struct team* new_node = create_new_node(team_id, team_name, school_name, competition_type, participants, instructor);
        
        // 如果链表还没创建,直接把新结点设为头结点
        if ((*head) == NULL) {
            (*head) = new_node;
            return;
        }
        
        // 找到链表尾部, 插入新的结点
        struct team* temp = (*head);
        while (temp->next != NULL) {
            temp = temp->next;
        }
        temp->next = new_node;
    }
     
    // 根据参赛队编号删除参赛队伍信息
    void delete_team(struct team** head, int team_id) {
        struct team* temp = (*head);
        struct team* prev = NULL;
        
        // 找到要删除的结点,如果找到,把 prev 指针指向 prev->next,跳过该结点
        while (temp != NULL && temp->team_id != team_id) {
            prev = temp;
            temp = temp->next;
        }
        
        // 如果没找到要删除的结点,则直接返回
        if (temp == NULL) {
            return;
        }
        
        // 删除头结点
        if (prev == NULL) {
            (*head) = temp->next;
            free(temp);
            return;
        }
        
        // 删除非头结点
        prev->next = temp->next;
        free(temp);
    }
     
    

    二.参赛队伍查找模块:

    • 从team.txt文件中读取参赛队伍的基本信息,构建二叉排序树。提供按参赛队编号查找的功能,根据输入的编号在二叉排序树中进行查找,如果查找成功,输出基本信息和平均查找长度(ASL);否则输出"查找失败!"。
    • // 参赛队伍信息的结构体定义
      struct team {
          int team_id;              // 参赛队编号
          string team_name;         // 参赛作品名称
          string school_name;       // 参赛学校
          int competition_type;     // 赛事类别代码
          string participants;      // 参赛者
          string instructor;        // 指导老师
          struct team* next;        // 指向下一个结点的指针
      };
       
      // 创建一个新结点并初始化它
      struct team* create_new_node(int team_id, string team_name, string school_name, int competition_type, string participants, string instructor) {
          struct team* new_node = new team;
          new_node->team_id = team_id;
          new_node->team_name = team_name;
          new_node->school_name = school_name;
          new_node->competition_type = competition_type;
          new_node->participants = participants;
          new_node->instructor = instructor;
          new_node->next = NULL;
          return new_node;
      }
       
      // 添加一个新结点到链表中
      void add_team(struct team** head, int team_id, string team_name, string school_name, int competition_type, string participants, string instructor) {
          struct team* new_node = create_new_node(team_id, team_name, school_name, competition_type, participants, instructor);
          
          // 如果链表还没创建,直接把新结点设为头结点
          if ((*head) == NULL) {
              (*head) = new_node;
              return;
          }
          
          // 找到链表尾部, 插入新的结点
          struct team* temp = (*head);
          while (temp->next != NULL) {
              temp = temp->next;
          }
          temp->next = new_node;
      }
       
      // 根据参赛队编号删除参赛队伍信息
      void delete_team(struct team** head, int team_id) {
          struct team* temp = (*head);
          struct team* prev = NULL;
          
          // 找到要删除的结点,如果找到,把 prev 指针指向 prev->next,跳过该结点
          while (temp != NULL && temp->team_id != team_id) {
              prev = temp;
              temp = temp->next;
          }
          
          // 如果没找到要删除的结点,则直接返回
          if (temp == NULL) {
              return;
          }
          
          // 删除头结点
          if (prev == NULL) {
              (*head) = temp->next;
              free(temp);
              return;
          }
          
          // 删除非头结点
          prev->next = temp->next;
          free(temp);
      }
       
      // 根据参赛队编号修改参赛队伍信息
      void update_team(struct team** head, int team_id, string team_name, string school_name, int competition_type, string participants, string instructor) {
          struct team* temp = (*head);
          
          // 找到要修改的结点,并修改它的信息
          while (temp != NULL && temp->team_id != team_id) {
              temp = temp->next;
          }
          if (temp != NULL) {
              temp->team_name = team_name;
              temp->school_name = school_name;
              temp->competition_type = competition_type;
              temp->participants = participants;
              temp->instructor = instructor;
          }
      }

三.参赛团队查询模块:

提供按参赛学校查询参赛团队的功能,根据输入的学校名称查询参赛的所有团队的基本信息,并按赛事类别进行排序输出。
提供按赛事类别查询参赛团队的功能,根据输入的赛事类别查询参赛的所有团队的基本信息,并按赛事类别进行排序输出。选择排序算法进行排序,因为在数据规模较小的情况下,选择排序简单且性能良好。

//定义二叉排序树节点类
class BSTNode {
public:
  string teamName;
  string schoolName;
  string race;
  //其他基本信息
 
  BSTNode* left;
  BSTNode* right;
  
  BSTNode(string teamName, string schoolName, string race){
    this->teamName = teamName;
    this->schoolName = schoolName;
    this->race = race;
    //初始化其他信息
 
    left = nullptr;
    right = nullptr;
  }
};
 
//定义二叉排序树类
class BST {
public:
  BSTNode* root;
 
  BST(){
    root = nullptr;
  }
 
  void insert(BSTNode* node){
    if(root == nullptr){
      root = node;
      return;
    }
 
    BSTNode* curr = root;
    while(true){
      if(node->teamName < curr->teamName){
        if(curr->left == nullptr){
          curr->left = node;
          break;
        }
        else
          curr = curr->left;
      }
      else{
        if(curr->right == nullptr){
          curr->right = node;
          break;
        }
        else
          curr = curr->right;
      }
    }
  }
};
 
//读取team.txt并构建二叉排序树
BST readTeamsFromFile(string fileName){
  BST bst;
  ifstream fin(fileName);
 
  string line;
  while(getline(fin, line)){
    //解析line并创建节点
    BSTNode* node = new BSTNode(teamName, schoolName, race);
    bst.insert(node);
  }
 
  fin.close();
  return bst;
}
 
//根据学校名称查询团队
vector<BSTNode*> searchBySchool(BSTNode* node, string schoolName){
  vector<BSTNode*> result;
  
  if(node == nullptr)
    return result;
 
  if(node->schoolName == schoolName){
    result.push_back(node);
  }
 
  vector<BSTNode*> subResultLeft = searchBySchool(node->left, schoolName);
  vector<BSTNode*> subResultRight = searchBySchool(node->right, schoolName);
  result.insert(result.end(), subResultLeft.begin(), subResultLeft.end());
  result.insert(result.end(), subResultRight.begin(), subResultRight.end());
 
  return result;
}
 
//根据赛事类别查询团队
vector<BSTNode*> searchByRace(BSTNode* node, string race){
  vector<BSTNode*> result;
  
  if(node == nullptr)
    return result;
 
  if(node->race == race){
    result.push_back(node);
  }
 
  vector<BSTNode*> subResultLeft = searchByRace(node->left, race);
  vector<BSTNode*> subResultRight = searchByRace(node->right, race);
  result.insert(result.end(), subResultLeft.begin(), subResultLeft.end());
  result.insert(result.end(), subResultRight.begin(), subResultRight.end());
 
  return result;
}
 
//按赛事类别对查询结果进行归并排序并输出
void mergeSortByRace(vector<BSTNode*>& teams){
  //实现归并排序并输出结果
}
 
//调用函数查询并输出结果
int main(){
  BST bst = readTeamsFromFile("team.txt");
 
  //根据学校名称查询并输出
  vector<BSTNode*> result1 = searchBySchool(bst.root, "清华大学");
  mergeSortByRace(result1);
  
  //根据赛事类别查询并输出
  vector<BSTNode*> result2 = searchByRace(bst.root, "篮球");
  mergeSortByRace(result2);
 
  return 0;
}


四.决赛叫号系统模块:

将参赛队伍按赛事类别分配到9个决赛室,按顺序叫号进场。
模拟决赛叫号过程,显示各决赛室的参赛队进场情况,确保叫号顺序与进场秩序一致。


#include <iostream>
#include <queue>
#include <vector>
 
using namespace std;
 
const int N_GROUP = 9;       // 参赛队伍分组数
const int N_PER_GROUP = 20;  // 每个组参赛队伍数量
const int N_ROOM = 5;        // 决赛室数量
 
int main()
{
    // 定义队列,存储各个决赛室的参赛队编号
    queue<int> rooms[N_ROOM];
    for (int i = 0; i < N_GROUP; i++) {
        // 将每个组的参赛队编号平均放入到对应的决赛室队列中
        for (int j = 0; j < N_PER_GROUP / N_ROOM; j++) {
            for (int k = 0; k < N_ROOM; k++) {
                rooms[k].push(i * N_PER_GROUP / N_ROOM + j + 1);
            }
        }
    }
 
    // 循环叫号进场
    for (int i = 0; i < N_PER_GROUP / N_ROOM; i++) {
        for (int j = 0; j < N_ROOM; j++) {
            // 取出当前决赛室队列队首的参赛队编号,进场
            int teamNo = rooms[j].front();
            cout << "第" << teamNo << "支参赛队伍进入第" << (j + 1) << "个决赛室" << endl;
 
            // 将该参赛队伍放到队列尾,等待下一次轮流进场
            rooms[j].pop();
            rooms[j].push(teamNo);
        }
 
        // 等待当前比赛结束
        cout << "等待当前比赛结束..." << endl;
        // 此处可以模拟比赛耗时
    }
 
    return 0;
}


五.校园导游程序模块:

提供校园地图中不少于10个目标地的导航查询服务,参赛者可以查询任意两个目的地之间的最短路径,并查询任意目标地的相关信息。
使用合适的图数据结构表示校园地图,例如图的邻接表表示法。
使用迪杰斯特拉算法计算最短路径,根据参赛者输入的起始点和目标点,输出最短路径和相关信息。


#include <iostream>
#include <vector>
#include <queue>
#include <climits>
 
using namespace std;
 
int get_index(vector<string>& nodes, string target) {
    for (int i = 0; i < nodes.size(); i++) {
        if (nodes[i] == target) {
            return i;
        }
    }
    return -1;
}
 
void build_map(vector<string>& nodes, vector<vector<int>>& map) {
    // 建立地点名称和编号之间的映射关系
    int node_count = nodes.size();
    vector<vector<int>> new_map(node_count, vector<int>(node_count, INT_MAX));
    for (int i = 0; i < node_count; i++) {
        new_map[i][i] = 0;
    }
    nodes.push_back("出口"); // 添加一个出口节点
    new_map.push_back(vector<int>(node_count+1, INT_MAX));
    for (int i = 0; i < node_count+1; i++) {
        new_map[i][node_count] = new_map[node_count][i] = INT_MAX;
    }
 
    // 添加建筑物和路径数据,建立地图
    new_map[0][1] = new_map[1][0] = 300; // 添加1号楼到2号楼的路径
    new_map[0][3] = new_map[3][0] = 500; // 添加1号楼到3号楼的路径
    new_map[1][4] = new_map[4][1] = 150; // 添加2号楼到4号楼的路径
    new_map[2][6] = new_map[6][2] = 200; // 添加教学楼到6号楼的路径
    new_map[3][4] = new_map[4][3] = 400; // 添加3号楼到4号楼的路径
    new_map[4][6] = new_map[6][4] = 350; // 添加4号楼到6号楼的路径
    new_map[4][7] = new_map[7][4] = 450; // 添加4号楼到7号楼的路径
    new_map[5][6] = new_map[6][5] = 150; // 添加图书馆到6号楼的路径
    new_map[5][7] = new_map[7][5] = 300; // 添加图书馆到7号楼的路径
 
    // 更新原图
    nodes = nodes; // 后续还需要添加其他建筑物
    map = new_map;
}
 
void dijkstra(vector<string>& nodes, vector<vector<int>>& map, int start, int end) {
    int node_count = nodes.size();
 
    // 初始化距离数组
    vector<int> dist(node_count, INT_MAX);
    dist[start] = 0;
 
    // 初始化访问数组
    vector<bool> visited(node_count, false);
 
    // 初始化路径数组
    vector<int> path(node_count, -1);
 
    // 依次访问每个节点
    for (int i = 0; i < node_count; i++) {
        // 从未访问节点中选取距离起点最近的节点
        int min_dist = INT_MAX;
        int current = -1;
        for (int j = 0; j < node_count; j++) {
            if (!visited[j] && dist[j] < min_dist) {
                min_dist = dist[j];
                current = j;
            }
        }
        if (current == -1) {
            break; // 所有节点均已访问过,退出循环
        }
        visited[current] = true; // 标记当前节点已访问
        // 以当前节点为中心,更新所有邻接节点的距离
        for (int k = 0; k < node_count; k++) {
            if (map[current][k] != INT_MAX) {
                int new_dist = dist[current] + map[current][k];
                if (new_dist < dist[k]) {
                    dist[k] = new_dist; // 更新到起点的最短距离
                    path[k] = current; // 通过哪个节点到达当前节点
                }
            }
        }
}
    // 根据path数组回溯路径
      if (path[end] != -1) {
        cout << nodes[end] << "<-";
        int p = path[end];
        while (p != start) { // 注意判断循环结束条件
            cout << nodes[p] << "<-";
            p = path[p];
        }
    // 输出起点
        cout << nodes[start] << endl;
    }
 
    // 继续输出路径上的节点
       if (p == start) {
            cout << nodes[start] << endl;
            break;
        }
        cout << nodes[p] << "<-";
        p = path[p];
    }
    // 输出最短距离
    cout << "最短距离为:" << dist[end] << endl;
}
 
int main() {
    vector<string> nodes = {"1号楼", "2号楼", "3号楼", "4号楼", "5号楼", "图书馆", "教学楼"};
    vector<vector<int>> map;
    build_map(nodes, map);
 
    // 查询任意两个建筑物之间的最短路径
    int start = get_index(nodes, "1号楼");
    int end = get_index(nodes, "教学楼");
    cout << "1号楼到教学楼的最短路径为:";
    dijkstra(nodes, map, start, end);
    
    start = get_index(nodes, "4号楼");
    end = get_index(nodes, "5号楼");
    cout << "4号楼到5号楼的最短路径为:";
    dijkstra(nodes, map, start, end);
 
    start = get_index(nodes, "图书馆");
    end = get_index(nodes, "出口");
    cout << "图书馆到出口的最短路径为:";
    dijkstra(nodes, map, start, end);
 
    return 0;
}

下面给出校园景点的无向带权图: 

 

 

 

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值