学生成绩档案管理系统
题目
学生信息录入,包括学号、姓名、专业、四门课成绩、总分、名次;
系统可对学生信息浏览、增加、删除和修改;
按学生成绩确定名次及信息输出,双向冒泡排序、希尔排序、快速排序、堆排序。
要求可对学生信息查询,根据学号或姓名进行查找;
信息修改仅可修改四门课成绩;
文件存取学生信息。
系统设计
使用Visual Studio 2019 开发环境完成相应C++主函数设计。
定义结构体,包含学生学号、姓名、专业、成绩、总分、名次。将友元函数声明为public类型,声明增加、浏览、查找、修改、删除、排序、保存这几个友元函数。
功能模块
增加模块(增加学生信息)
浏览模块(浏览信息)
查找模块(查找学生条件)
修改模块(修改学生成绩)
删除模块(删除学生的信息)
排序模块(双向冒泡排序、希尔排序、快速排序、堆排序)
保存模块(保存学生成绩信息)
排序算法
双向冒泡排序
比较相邻两个元素的大小。如果前一个元素比后一个元素大,则两元素位置交换
对数组中所有元素的组合进行第1步的比较
奇数趟时从左向右进行比较和交换
偶数趟时从右向左进行比较和交换
当从左端开始遍历的指针与从右端开始遍历的指针相遇时,排结束
希尔排序
初始时,有一个大小为 10 的无序序列。
在第一趟排序中,我们不妨设 gap1 = N / 2 = 5,即相隔距离为 5 的元素组成一组,可以分为 5 组。
接下来,按照直接插入排序的方法对每个组进行排序。
在第二趟排序中,我们把上次的 gap 缩小一半,即 gap2 = gap1 / 2 = 2 (取整数)。这样每相隔距离为 2的元素组成一组,可以分为 2 组
按照直接插入排序的方法对每个组进行排序。
在第三趟排序中,再次把 gap 缩小一半,即gap3 = gap2 / 2 = 1。 这样相隔距离为 1 的元素组成一组,即只有一组
按照直接插入排序的方法对每个组进行排序。
快速排序
首先设定一个分界值,通过该分界值将数组分成左右两部分
将大于或等于分界值的数据集中到数组右边,小于分界值的数据集中到数组的左边。此时,左边部分中各元素都小于或等于分界值,而右边部分中各元素都大于或等于分界值
然后,左边和右边的数据可以独立排序。对于左侧的数组数据,又可以取一个分界值,将该部分数据分成左右两部分,同样在左边放置较小值,右边放置较大值。右侧的数组数据也可以做类似处理
重复上述过程,可以看出,这是一个递归定义。通过递归将左侧部分排好序后,再递归排好右侧部分的顺序。当左、右两个部分各数据排序完成后,整个数组的排序也就完成了
堆排序
算法原理:
堆排序是指利用堆这种数据结构所设计的一种排序算法。堆是一个近似完全二叉树的结构,并同时满足堆积的性质:即子结点的键值或索引总是小于它的父节点。
算法步骤:
首先将待排序的数组构造成一个大根堆,此时,整个数组的最大值就是堆结构的顶端
将顶端的数与末尾的数交换,此时,末尾的数为最大值,剩余待排序数组个数为n-1
将剩余的n-1个数再构造成大根堆,再将顶端数与n-1位置的数交换,如此反复执行,便能得到有序数组
实现代码
scoreManage.h
#include <string>
#include<iostream>
using namespace std;
struct student {
int id;//学号
string name;//姓名
string major;//专业
double math_score;//高数成绩
double english_score;//英语成绩
double psy_score;//大物成绩
double major_score;//专业成绩
double total_score;//总分
int sortid = 1;
};
struct studentNode {
student stu;//包含学生信息
studentNode* next;
};
class scoreManage {
public:
studentNode* first;
public:
scoreManage();
~scoreManage();
void load_file();//加载文件信息
void save_file();//保存
int total();//计算学生总数
bool is_same(studentNode* bt);//判断学号是否重复的函数的声明
void add_student(studentNode* bt, int t = 1);//添加学生
void del_student(int id);//删除学生
void modify_score(double score);//修改成绩
studentNode* tool();//学生信息
studentNode* find_student(int id);//通过学号查找学生
studentNode* find_student(string name);//通过姓名查找
void print_one(studentNode* bt);//输出单个学生信息
void print();//输出所有学生信息
void sort_print();//输出排序后的学生信息
void bubble_sort();//双向冒泡排序
void Hill_sort();//希尔排序
void quick_sort(student a[], int low, int high);//快速排序
void heap_sort();//堆排序
student* linktoarray();//链表->数组
void arrytolink(student* stu);
private:
void heapp(student* a, int k, int n);//调整堆
};
void start();
void menu();
scoreManange.cpp
#include <iostream>
#include<stdio.h>
#include<fstream>
#include<conio.h>//catch() 任意键继续
#include <windows.h>//system("cls") 清屏
#pragma warning(disable:4996)
#include<iostream>
#include<fstream>
#include<iomanip>
#include<string>
#include<algorithm>
#include<sstream>
#include "scoreManage.h"
#include<string>
using namespace std;
scoreManage test;
/*
* 初始化,读取文件
*/
scoreManage::scoreManage() {
first = NULL;
load_file();
}
/*
* 对象调用结束
*/
scoreManage::~scoreManage() {
}
/*
* 判断学号是否重复(学号必须唯一)
*/
bool scoreManage::is_same(studentNode* bt) {
studentNode* p = first;
while (p)
{
if (p->stu.id == bt->stu.id)
{
return false;
}
p = p->next;
}
return true;
}
/*
* 将txt中数据读入链表中
*/
void scoreManage::load_file() {
FILE* fp;
studentNode* p = new studentNode;
fp = fopen("E://c//studentScore.txt", "r");//以可读方式打开文件
if (!fp)
{
printf("文件不存在\n");
start();
return;
}
//判断文件内容是否为空,通过读取,判断是否有
ifstream ifs("E://c//studentScore.txt", ios::in);
char ch;
ifs >> ch;
if (ifs.eof())
{
cout << "文件为空,请先添加!" << endl;
return;
}
ifstream in;
in.open("E://c//studentScore.txt");
string sline;//每一行
while (getline(in, sline)) //逐行读取txt文件
{
istringstream sin(sline);
sin >> p->stu.id >> p->stu.name >> p->stu.major >> p->stu.math_score >> p->stu.english_score >> p->stu.psy_score >> p->stu.major_score >> p->stu.total_score >> p->stu.sortid;
add_student(p, 0);
p = new studentNode;
}
}
/*
* 将一系列操作后的数据重新保存
*/
void scoreManage::save_file() {
studentNode* p;
p = first;
if (p == NULL)
{
printf("现在没有学生信息,请先输入学生信息\n\n");
studentNode* q = tool();
add_student(q);
return;
}
//先清空txt
fstream file("E://c//studentScore.txt", ios::out);
fstream in("E://c//studentScore.txt"); //打开txt
int c = 0;
while (p)
{
in << p->stu.id << setw(10) << p->stu.name << setw(10) << p->stu.major << setw(10) << p->stu.math_score << setw(10) << p->stu.english_score << setw(10) << p->stu.psy_score << setw(10) << p->stu.major_score << setw(10) << p->stu.math_score + p->stu.english_score + p->stu.psy_score + p->stu.major_score << setw(10) << p->stu.sortid;
in << '\n';
p = p->next;
}
in.close();
cout << "保存成功" << endl;
}
/*
* 计算学生总数
*/
int scoreManage::total() {
if (!first) {
cout << ' 0' << endl;
return 0;
}
int n = 0;
studentNode* p = first;
while (p->next != NULL)
{
n++;
p = p->next;
}
//cout << "一共有" << n + 1 << "个学生." << endl;
return n + 1;
}
/*
* 用于添加的学生信息
*/
studentNode* scoreManage::tool() {
studentNode* p = new studentNode;
cout << "请输入学号:" << endl;
int id;
cin >> id;
p->stu.id = id;
cout << "请输入姓名:" << endl;
string name;
cin >> name;
p->stu.name = name;
cout << "请输入专业:" << endl;
string major;
cin >> major;
p->stu.major = major;
cout << "高数成绩:" << endl;
double score1;
cin >> score1;
p->stu.math_score = score1;
cout << "英语成绩:" << endl;
double score2;
cin >> score2;
p->stu.english_score = score2;
cout << "大物成绩:" << endl;
double score3;
cin >> score3;
p->stu.psy_score = score3;
cout << "专业成绩:" << endl;
double score4;
cin >> score4;
p->stu.major_score = score4;
p->stu.total_score = score1 + score2 + score3 + score4;
return p;
}
/*
* 添加学生信息
*/
void scoreManage::add_student(studentNode* bt, int t) {
studentNode* p = new studentNode;
if (first == NULL)//如果链表为空
{
first = bt;
bt->next = NULL;
return;//插入完成返回
}
else {
if (is_same(bt))//判断学号是否重复
{
p = first;//从头结点开始找
while (p)//p非空则一直循环
{
if (p->next == NULL)//找到尾节点
{
p->next = bt;
bt->next = NULL;
return;//插入完成返回
}
p = p->next;//找下一个节点
}
}
else {
cout << "学号重复!此学生信息添加失败" << endl;
if (t == 0) {
return;
}
if (t == 1) {
cout << "是否重新输入:(1-yes 2-no)" << endl;
int d;
cin >> d;
switch (d)
{
case 1:
{studentNode* q = tool();
add_student(q);//重新添加
};
default:
break;
}
}
}
}
}
/*
* 删除某个学生信息
*/
void scoreManage::del_student(int id) {
studentNode* pre = new studentNode;//前驱指针
studentNode* p = first;
pre->next .= p;
while (p)
{
if (p->stu.id == id)//找到
{
pre->next = p->next;
p->next = NULL;
delete p;
cout << "删除成功" << endl;
}
pre->next = p;
p = p->next;
}
cout << "未找到信息!" << endl;
}
/*
* 修改学生成绩,通过选择判断输入具体学科的成绩
*/
void scoreManage::modify_score(double score) {
studentNode* p;
cout << "查找学生的方式:(1-学号查找 2-姓名查找)" << endl;
int chioce;
cin >> chioce;
switch (chioce)
{
case 1: {
cout << "请输入学号:" << endl;
int name;
cin >> name;
p = find_student(name);
if (!p) {
cout << "未找到相关信息!" << endl;
modify_score(score);
}
else {
cout << "选择要修改的学科:(1-高数 2-英语 3-大物 4-专业)" << endl;
int c;
ci.n >> c;
switch (c) {
case 1:
p->stu.math_score = score;
cout << "修改成功" << endl;
break;
case 2:
p->stu.english_score = score;
cout << "修改成功" << endl;
break;
case 3:
p->stu.psy_score = score;
cout << "修改成功" << endl;
break;
case 4:
p->stu.major_score = score;
cout << "修改成功" << endl;
break;
default:
cout << "无效字符" << endl;
modify_score(score);
break;
}
p->stu.total_score = p->stu.math_score + p->stu.english_score + p->stu.psy_score + p->stu.major_score;
}
}
break;
case 2: {
cout << "请输入姓名:" << endl;
string name;
cin >> name;
p = find_student(name);
if (!p) {
cout << "未找到相关信息!" << endl;
modify_score(score);
}
else {
cout << "选择要修改的学科:(1-高数 2-英语 3-大物 4-专业)" << endl;
int c;
cin >> c;
switch (c) {
case 1:
p->stu.math_score = score;
break;
case 2:
p->stu.english_score = score;
break;
case 3:
p->stu.psy_score = score;
break;
case 4:
p->stu.major_score = score;
break;
default:
cout << "无效字符" << endl;
modify_score(score);
break;
}
p->stu.total_score = p->stu.math_score + p->stu.english_score + p->stu.psy_score + p->stu.major_score;
}
}
break;
default:
cout << "无效字符" << endl;
cout << "是否重新输入:(1-yes 2-no)" << endl;
int d;
cin >> d;
switch (d)
{
case 1:
modify_score(score);//重新继续输入
default:
break;
}
break;
}
}
studentNode* scoreManage::find_student(int id) {
studentNode* p = new studentNode;
p = first;
while (p)
{
if (p->stu.id == id)
{
return p;
}
p = p->next;
}
return NULL;
}
studentNode* scoreManage::find_student(string name) {
studentNode* p = new studentNode;
p = first;
while (p)
{
if (strcmp(name.c_str(), p->stu.name.c_str()) == 0)//必须是字符数组
{
return p;
}
p = p->next;
}
return NULL;
}
void scoreManage::print_one(studentNode* bt) {
if (!bt) {
cout << "未找到相关信息!" << endl;
}
else {
cout << "查找成功" << endl;
cout << "学号\t姓名\t专业\t高数成绩\t英语成绩\t大物成绩\t专业成绩\t总分成绩\t\t排名" << endl;
cout << bt->stu.id << '\t' << bt->stu.name << '\t' << bt->stu.major << '\t' << bt->stu.math_score << "\t " << bt->stu.english._score << "\t " << bt->stu.psy_score << "\t " << bt->stu.major_score << "\t " << bt->stu.total_score << "\t " << bt->stu.sortid << endl;
}
}
/*
* 打印所有学生
*/
void scoreManage::print() {
if (!first) {
cout << "没有学生信息" << endl;
return;
}
studentNode* bt;
bt = first;
cout << "学号\t姓名\t专业\t高数成绩\t英语成绩\t大物成绩\t专业成绩\t总成绩\t\t排名" << endl;
while (bt) {
cout << bt->stu.id << '\t' << bt->stu.name << '\t' << bt->stu.major << '\.t' << bt->stu.math_score << "\t " << bt->stu.english_score << "\t " << bt->stu.psy_score << "\t " << bt->stu.major_score << "\t " << bt->stu.total_score << "\t " << bt->stu.sortid << endl;
bt = bt->next;
}
}
void scoreManage::sort_print() {
print();
}
/*
*/
student* scoreManage::linktoarray() {
studentNode* p;
p = first;
int n = total();
student* stu = new student[n];
if (!first) {
cout << "没有学生" << endl;
return NULL;
}
for (int i = 0;i < n;i++) {
stu[i] = p->stu;
p =. p->next;
}
return stu;
}
/*
*/
void scoreManage::arrytolink(student* stu) {
studentNode* p;
p = first;
int n = total();
for (int j = 0;j < n;j++) {
while (p) {
if (stu[j].id == p->stu.id) {//为链表中的sortid赋值 排序序号
p->stu.sortid = n - j;
}
p = p->next;
}.
p = first;//p指向头节点
}
}
/*
* 双向冒泡排序
*/
void scoreManage::bubble_sort() {
student* arr;//学生数组
arr = linktoarray();//有学生链表得到学生数组
int n = total();//链表长度
int flag = 1;int i = 0;//flag=1有交换,flag=0无交换
while (flag == 1)
{
flag = 0;
for (int j = n - i - 1;j > i;j--)//从后往前比较
{
if (arr[j - 1].total_score > arr[j].total_score)
{
flag = 1;
student tmp = arr[j];
arr[j] = arr[j - 1];
arr[j - 1] = tmp;
}
}
for (int j = i + 1;j < n - i - 1;j++)//从前往后比较
{
if (arr[j].total_score > arr[j + 1].total_score)
{
flag .= 1;
student tmp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = tmp;
}
}
i++;
}
arrytolink(arr);
}
void scoreManage::Hill_sort() {
student* arr;
arr = linktoarray();//链表数组
int n = total();//链表长度
int j, i;.
student temp;
int divide = ceil(n / 2);//取长度一半,取整
for (divide;divide >= 1; divide = ceil(divide / 2)) {//对每个h分组进行插入排序,直到h==0
for (i = divide;i < n;i++) {//单次插入排序
if (arr[i].total_sco.re < arr[i - divide].total_score) {
temp = arr[i];
// 将A[i]插入到A[i - h],A[i - 2h]...中
for (j = i - divide;j >= 0 && arr[j].total_score > temp.total_score;j = j - divide) {
arr[j + divide] = arr[j];
}
arr[j + divide] = temp;
}
}
}
arrytolink(arr);
}
void scoreManage::quick_sort(student* a, int low, int high) {
if (low < high) //判断是否满足排序条件,递归的终止条件
{
int i = low, j = high; //把待排序数组元素的第一个和最后一个下标分别赋值给i,j,使用i,j进行排序;
student x = a[low]; //第一个元素作为哨兵,将数组划分为大于哨兵以及小于哨兵的两部分
while (i < j)
{
while (i < j && a[j].total_score >= x.total_score) j--;
if (i < j)
{
a[i++] = a[j];
}
while (i < j && a[i].total_score <= x.total_score) i++;
if (i < j) {
a[j--] = a[i];
}//把不满足位次条件的那个元素值赋值给下标为j的元素,并把j的加1
}
a[i] = x; //完成一次排序,把哨兵赋值到下标为i的位置,即前面的都比它小,后面的都比它大
quick_sort(a, low, i - 1); //递归进行哨兵前后两部分元素排序 , low,high的值不发生变化,i处于中间
quick_sort(a, i + 1, high);
}
}
/*
*构造小根堆
* */
void scoreManage::heapp(student* a, int i, int n) {
int j = 2 * i + 1;//找到当前结点的左孩子
student temp = a[i];//为值转化做准备
while (j < n) {
if (j + 1 < n && a[j].total_score < a[j + 1].total_score) ++j;//确保与父节点交换的是最大的孩子
if (temp.total_score > a[j].total_score) break;
else {
a[i] = a[j];
i = j;
j = 2 * i + 1;
}
}
a[i] = temp;
}
/*
* 堆排序
*/
void scoreManage::heap_sort() {
student* a;
a = linktoarray();//链表数组
int n = total();//链表长度
for (int i = n / 2 - 1; i >= 0; i--)
heapp(a, i, n);
for (int i = n - 1; i >= 1; i--) {//逆序
student t;
t = a[0];
a[0] = a[i];
a[i] = t;
heapp(a, 0, i);
}
arrytolink(a);
}
void menu() {
printf(" || ☆☆☆☆☆☆☆☆请选择相应功能
1 添加学生成绩信息 ||\n");
2 查看所有学生成绩信息
3 按学号查找学生成绩信息
4 按姓名查找学生成绩信息
5 删除学生成绩信息
6 修改学生成绩信息
7 选择排序方法
8 保存所有学生信息
9 查看学生人数
10退出系统 ||\n\n");
void start() {
int choose;
menu();
cout << "请输入选择:" << endl;;/*用户的选择*/
cin >> choose;
switch (choose)
{
case 1:
{
system("cls");//清屏
studentNode* p = test.tool();
test.add_student(p);
cout << "按任意键继续" << endl;
getch();
start();
}
break;
case 2:
{
system("cls");//清屏
test.print();
cout << "按任意键继续" << endl;
getch();
start();
}
break;
case 3:
{
system("cls");//清屏
cout << "请输入学号:" << endl;
int id;
cin >> id;
studentNode* p = test.find_student(id);
test.print_one(p);
cout << "按任意键继续" << endl;
getch();
start();
}
break;
case 4:
{
system("cls");//清屏
cout << "请输入name:" << endl;
string name;
cin >> name;
studentNode* p = test.find_student(name);
test.print_one(p);
cout << "按任意键继续" << endl;
getch();
start();
}
break;
case 5:
{
system("cls");//清屏
cout << "请输入要删除学生的学号" << endl;
int id;
cin >> id;
test.del_student(id);
cout << "按任意键继续" << endl;
getch();
start();
}
break;
case 6:
{
system("cls");//清屏
cout << "请输入修改的成绩:(先输入成绩,在选择学生)" << endl;
double score;
cin >> score;
test.modify_score(score);
cout << "按任意键继续" << endl;
getch();
start();
}
break;
case 7:
{
system("cls");//清屏
cout << "请选择要采用的排序算法:(1-双向冒泡排序 2-希尔排序 3-快速排序 4-堆排序)" << endl;
int chioce;
cin >> chioce;
switch (chioce)
{
case 1:.
test.bubble_sort();
cout << "排序完成" << endl;
// start();
break;
case 2:
test.Hill_sort();
cout << "排序完成" << endl;
// start();
break;
case 3:
{
student* arr;
arr = test.linktoarray();
int n = test.total();
test.quick_sort(arr, 0, n - 1);
test.arrytolink(arr);
}
cout << "排序完成" << endl;
//start();
break;
case 4:
{
test.heap_sort();
cout << "排序完成" << endl;
}
break;
default:
cout << "无效字符" << endl;
break;
}
cout << "按任意键继续" << endl;
getch();
start();
}
break;
case 8:
system("cls");//清屏
test.save_file();
cout << "按任意键继续" << endl;
getch();
start();
break;
case 9:
cout << "一共有" << test.total() << "个学生" << endl;;
cout << "按任意键继续" << endl;
getch();
start();
break;
case 10:
cout << "再见" << endl;
exit(0);
break;
default:
cout << "无效符号" << endl;
start();
break;
}
}