C++数据结构实验一:线性表

目录

一、实验目的

二、问题分析及数据结构设计

三、算法设计

四、功能模块程序流程图

五、实验结果

六、算法分析

七、操作说明

八、源代码


近来有空闲,把前几个学期做的实验报告上传上来。如有错误的地方欢迎大佬批评指正,有更好的方法也期待您的分享~


实验要求

编写一个实验程序实现以下功能:

有一个学生成绩文本文件xyz4.in,第一行为整数n,接下来为n行学生基本信息,包括学号、姓名和班号,接下来为整数m,然后为m行课程信息,包括课程编号和课程名,再接下来为整数k,然后为k行学生成绩,包括学号、课程编号和分数。编写一个程序按班号递增排序输出所有学生的成绩,相同班号按学号递增排序,同一个学生按课程编号递增排序,相邻的班号和学生信息不重复输出。


一、实验目的

1.熟悉线性表的逻辑结构、线性表抽象数据类型的描述方法;

2.熟悉存储结构以及算法设计方法;

3.熟悉线性表的建立、插入操作;

4.熟练 STL 中的 vector 容器及其应用;

5.复习文件读写的基本操作。

二、问题分析及数据结构设计

本次开发任务要求我们读取文件建立学生成绩线性表,并对其进行按某域值递增排序的操作。该任务涵盖功能要求有文件读取学生基本信息、课程信息和学生成绩以及建立线性表、按照班号递增排序输出所有学生的成绩,同时相同班号的学生按学号递增排序,同一个学生的成绩按课程编号递增排序。

从实验要求可知我们并不需要后续的交互, STL 标准库比我们自己实现的算法更健壮高效,在我们了解数据结构及其操作的基础上,直接使用标准库不用重复造轮子,可以提高工作效率,因此本次开发采用 STL 。 STL 组件包括容器、迭代器、算法三部分, STL 的基本观念是将数据与操作分离,数据由容器类管理,操作则由可定制的算法负责,迭代器在两者之间充当粘合剂,使任何算法都可以和任何容器交互运作。

首先我们需要选择容器,因为是线性表,自然选择顺序容器,有 vector 、 string 、 deque和 list ,因为不需要后续的插入与删除操作,故选择 vector 向量容器。

其次是数据结构设计,我们需要建立结构体方便储存从文件中读取的信息。根据实验要求,我们建立学生与课程两个结构体。课程信息结构体 Course 包含的数据项有课程编号、课程名和成绩,因为这3种都是字符串,所以使用 string 来分别定义为 id 、 name 和 score ;学生信息结构体Student包含的数据项有学号、姓名、班号和成绩,因为前三种数据都是字符串,所以使用 string 来分别定义为 id 、 name 和 classNum ,而成绩有多项数据,因此定义为课程信息结构体 Course 的 vector 容器 score 。同时为了保存学生信息和课程信息,创建包含 Student 结构体类型的 vector 向量容器 studentList 和包含 Course 结构体类型的 vector 向量容器 courseList 。

三、算法设计

1.读入文件功能

本实验要求从文本文件中读取学生成绩到线性表中,涉及的算法为 C++ 文件读取。

1.1第一步:写头文件 #include <fstream>

要在 C++ 中进行文件处理,必须在 C++ 源代码文件中包含头文件 <iostream> 和 <fstream> 。 iostream  标准库提供了 cin 和 cout 方法分别用于从标准输入读取流和向标准输出写入流。fstream 标准库定义了三个数据类型: ofstream 、 ifstream 、 fstream ,可以创建文件,向文件写入信息,从文件读取信息。

1.2第二步:采用open() 函数打开文件

本次实验只需要打开文件进行读操作,则使用 ifstream 对象。随后采用 open() 函数打开文件。open() 函数标准语法为:

void open(const char *filename, ios::openmode mode);

在这里, open() 成员函数的第一参数指定要打开的文件的名称和位置,第二个参数定义文件被打开的模式。

模式标志

描述

ios::app

追加模式。所有写入都追加到文件末尾。

ios::ate

文件打开后定位到文件末尾。

ios::in

打开文件用于读取。

ios::out

打开文件用于写入。

ios::trunc

如果该文件已经存在,其内容将在打开文件之前被截断,即把文件长度设为 0。

由于本次实验只需要打开文件进行读操作,因此定义文件被打开的模式为 ios::in。

1.3第三步:使用流提取运算符( >> )从文件读取信息

在 C++ 编程中,我们使用流提取运算符( >> )从文件读取信息,就像使用该运算符从键盘输入信息一样。唯一不同的是,在这里使用的是 ifstream 或 fstream 对象,而不是 cin 对象。本题使用的是fstream 对象。详细读取部分算法分析见2.3部分。

1.4第四步:使用close() 函数关闭文件

    close() 函数是 fstream 、 ifstream 和 ofstream 对象的一个成员, close() 函数的标准语法为:

void close();
1.5读入文件功能的伪代码:

打开文件(file)

如果文件打开成功,则

       创建一个缓冲区(buffer)来存储读取的数据

       初始化数据计数器(count)为0

       重复执行直到文件末尾:

                从文件中读取数据到缓冲区

                增加数据计数器

                 处理缓冲区中的数据

        关闭文件

否则,

        报告文件打开错误

2.vector 容器添加元素、获取迭代器等功能

本实验要求将学生基本信息、课程信息和学生成绩建立线性表来存储,涉及 C++ 顺序容器 vector 算法。

vector 是一个能够存放任意类型的动态数组,能够增加和压缩数据。因为是动态数组,当我们无法知道自己需要的数组的规模多大时,用其来解决问题可以达到最大节约空间的目的。 vector 也是一个类模板( class template )。模板允许我们编写单个类或函数定义,这个类和函数定义可用于不同的数据类型上。因此,我们可以定义保存 string 对象的 vector ,或保存 int 值的 vector ,又或是保存自定义的类类型对象的 vector 。

图1 vector容器原理图
2.1第一步:写头文件 #include <vector> ,使用全局命名域方式 using namespace std

    或者也可以使用 using std::vector; 。

2.2创建 vector 对象 studentList 和 courseList

    必须说明 vector 保存何种对象的类型,通过将类型放在类模板名称后面的尖括号中来指定类型,例如 vector<int> vec; 。本题类型为我们自己定义的类型 Student 和 Course 。

2.3使用 vec.push_back(a) 添加元素

push_back() 函数将一个新的元素加到 vector 的最后面,位置为当前最后一个元素的下一个元素。

以本次实验的读取学生成绩为例,首先定义局部变量n为学生人数,读取学生人数。

随后定义一个结构体Student类型的student变量,使用流提取运算符( >> )从文件读取信息,然后采用 studentList.push_back(student) 添加成绩元素,以n--为条件进行while循环来循环初始化学生信息。

2.4结合获取迭代器使用sort函数对学生信息升序排列

vec.begin() 是返回vec的首元素; vec.end() 是返回尾元素之后元素的位置,即最后一个单元+1的指针,获取迭代器得到第一位学生与最后一位学生的指针,结合 sort() 函数来实现对学生信息升序排列。详细 sort() 排序函数见3.3部分。

2.5vector容器添加元素、获取迭代器等功能伪代码为:

声明一个vector容器

while循环初始化学生信息

       定义一个结构体变量

       读取变量信息

       向容器中添加元素

定义一个vector容器的迭代器

for循环

使用迭代器遍历vector容器的所有元素

3.vector排序功能

在本次实验中,我们需要按照班号递增排序输出所有学生的成绩,同时相同班号的学生按学号递增排序,同一个学生的成绩按课程编号递增排序。对于 C++ 的排序, C++ 标准库里有排序函数。排序函数 sort() 就是最常用的。

3.1写头文件#include<algorithm>

在 C++ 中使用 sort() 函数需要使用 #include<algorithm> 头文件。 algorithm 意为"算法",是 C++ 的标准模版库(STL)中最重要的头文件之一,提供了大量基于迭代器的非成员模版函数。

3.2排序函数sort()基本使用方法

  sort函数可以对给定区间所有元素进行排序。 sort() 函数标准语法为:

sort(begin, end, cmp)

其中 begin 为指向待 sort() 的数组的第一个元素的指针,end为指向待 sort() 的数组的最后一个元素的下一个位置的指针, cmp 参数为排序准则, cmp 参数可以不写,如果不写的话,默认从小到大进行排序。

3.3自定义排序准则

    sort() 函数可以自定义排序准则,以便满足不同的排序情况。

    在本次实验中,我们自定义3个辅助排序函数,分别是学号升序排序函数 num_sort(Student a, Student b) 、班号升序排序函数 cla_sort(Student a, Student b) 、课程编号升序排序函数cou_sort(Course a, Course b)。

在从文件读取信息时,使用 sort() 函数排序的准则就写我们自定义的3个辅助排序函数。

3.4 vector 排序功能伪代码:

自定义排序准则

声明一个 vector 容器

对容器进行自定义准则排序

四、功能模块程序流程图

图2 功能模块程序流程图

五、实验结果

向txt文件中输入数据:

5

1 陈斌 101

3 王辉 102

5 李君 101

4 鲁明 101

2 张昂 102

3

2 数据结构

1 C程序设计

3 计算机导论

15

1 1 82

4 1 78

5 1 85

2 1 90

3 1 62

1 2 77

4 2 86

5 2 84

2 2 88

3 2 80

1 3 60

4 3 79

5 3 88

2 3 86

3 3 90

经过文件读取学生基本信息、课程信息和学生成绩以及建立线性表、按照班号递增排序输出所有学生的成绩,同时相同班号的学生按学号递增排序,同一个学生的成绩按课程编号递增排序的处理,得出实验结果如下:

图3  实验结果图

六、算法分析

1.时间复杂度 O(nlogn) : 其中n是线性表的长度。 总遍历轮数为 logn ~ n 次。

2.空间复杂度 O(logn) : 主要为递归的空间开销。

七、操作说明

1.本次实验程序输入的数据为学生成绩的 txt 文件, txt 文件的输入规范为:

        第一行为整数n,表示学生数,换行;

        接下来为n行学生基本信息,包括学号、姓名和班号,3种数据中间以空格区分;

        接下来为整数m,表示课程信息数,换行;

        接下来为m行课程信息,包括课程编号和课程名,2种数据中间以空格区分;

        再接下来为整数k,表示学生成绩数据量,换行;

        然后为k行学生成绩,包括学号、课程编号和分数,3种数据中间以空格区分。

2.输入完学生成绩后保存 txt 文件,运行程序,程序自动读取 txt 文件并进行排序操作;

3.输出结果格式为:

        第一行为按递增排序后第一位的班号,换行;

        随后是学号按递增排序后第一位学生的学号、姓名,以及按课程编号递增排序的课程名及成绩,4种数据间以空格区分,不同课程换行;

        接下来是同班学号排第二位学生的学号,接下来同理;

        第一个班级的学生信息输出完后,显示按递增排序后第二位的班号,换行。

        接下来同理。

        以语句“退出成功,欢迎下次使用!”作为程序输出结束的标志。

4.按任意键退出程序。

八、源代码

#include <iostream>  //io流输入输出
#include <iomanip>   //setw场宽函数
#include <vector>    //vector容器
#include <algorithm> // sort排序函数
#include <fstream>   //读文件

using namespace std;

struct Course  //课程信息结构体
{
    string id;    //课程编号
    string name;  //课程名
    string score; //成绩
};

struct Student  //学生信息结构体
{
    string id;            //学号
    string name;          //姓名
    string classNum;      //班号
    vector<Course> score; //成绩,定义一个结构体变量的vector容器
};

//自定义函数
void read();        //将文件中的数据读入到结构体数组中

//容器
vector<Student> studentList;//创建包含Student结构体类型的向量studentList
vector<Course> courseList;//创建包含Course结构体类型的向量courseList

//三个辅助排序函数
//1_学号升序排序
bool num_sort(Student a, Student b)
{
    return a.id < b.id; //学号升序排列
}
//2_班号升序排序
bool cla_sort(Student a, Student b)
{
    if (a.classNum != b.classNum)
        return a.classNum < b.classNum; //如果班号不同则返回班号小的,进行班号升序排序
    else
        return a.id < b.id; //如果班号相同,则返回学号小的,进行学号升序排序
}
//3_课程编号升序排序
bool cou_sort(Course a, Course b)
{
    return a.id < b.id; //课程编号升序排列
}

//读取学生
//文件读
void read() {
    ifstream fin;
    fin.open("./exp1.txt", ios::in); //创建读取文件的流
    if (!fin.is_open()) //若文件打开失败
    {
        cout << "无法找到这个文件!" << endl;//反馈打开文件失败
        return;
    }

    puts("   欢迎进入学生管理系统   "); //成功打开文件

    //读取学生
    int n; //学生人数
    fin >> n; //读取学生人数
    while (n--) //循环读取学生信息,初始化学生信息
    {
        Student student; //新建结构体对象
        fin >> student.id; //读取学号
        fin >> student.name; //读取姓名
        fin >> student.classNum; //读取班号
        studentList.push_back(student);//向向量中添加结构体对象
    }
    sort(studentList.begin(), studentList.end(), num_sort); // 对每一个初始化的学生信息从头到尾使用sort进行学号升序排列

    //读取课程
    int m; //课程数量
    fin >> m; //读取课程数量
    while (m--) //初始化课程信息
    {
        Course course; //新建结构体对象
        fin >> course.id; //读取课程编号
        fin >> course.name; //读取课程名
        courseList.push_back(course); //向向量中添加结构体对象
    }
    sort(courseList.begin(), courseList.end(), cou_sort); //课程升序排序

    //读取学生成绩
    int k; //学生成绩数量
    fin >> k; //读取学生成绩数量
    while (k--) //初始化学生成绩信息
    {
        Course course; //新建结构体对象
        int studentId; //定义局部变量学生学号
        fin >> studentId >> course.id >> course.score;      //读取学号、课程编号、成绩
        studentList[studentId - 1].score.push_back(course); //别忘了索引值是从0开始的
    }

    //后续处理  使用迭代器遍历向量中的结构体对象,将每个学生的成绩按课程编号升序排好
    for (auto x : studentList) {
        sort(x.score.begin(), x.score.end(), cou_sort);
    }
    sort(studentList.begin(), studentList.end(), cla_sort); //重新按班号排序学生
    //输出
    string tag = "";
    for (auto x : studentList)//使用迭代器遍历向量中的结构体对象,输出学生成绩信息
    {
        if (x.classNum != tag) //第一论:如果班号不为空,则输出班级的学生成绩信息;后面几轮,如果班号相同,则输出同一班级学生成绩信息
        {
            cout << "===============班号:" << x.classNum << "===============" << endl;
            tag = x.classNum;
        }
        cout << x.id << " " << x.name << " " << endl;//输出学号、姓名
        for (int i = 0; i < courseList.size(); i++)//输出课程成绩
        {
            cout << courseList[i].name;//输出课程名
            cout << setiosflags(ios::right) << setw(20 - courseList[i].name.size()) << x.score[i].score << endl;//输出课程成绩
        }

        cout << endl;//输出完一个学生的成绩信息,换行
    }

    cout << "退出成功,欢迎下次使用!" << endl;//退出系统提示
    fin.close(); //关闭文件
}



//主函数
int main()
{
    read();    //将文件中的学生信息读取到容器中
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值