成绩分析系统(数据结构)作业记录
1. 题目说明
成绩分析系统
建立一个学生链表,每个结点包括学号、姓名、班级、语文数学英语三门课程成绩等数据项。实现如下功能(设计菜单):
(1)成绩录入。学生的基本信息(学号、姓名、班级)应通过其他文件成批导入;学生的成绩通过键盘输入和修改,最终的完整数据应能保存到数据文件中input.dat,并能从文件中导入已有数据。
(2)按指定的课程成绩排序。
输入指定的课程名,按指定课程的成绩进行排序,生成相应的文件并输出。
(3)按学生的平均成绩排序。
按学生的平均成绩进行排序,生成相应的文件并输出。
(4)成绩分析与统计。
统计每门课程的平均成绩、最高分、最低分、不及格人数、60~69分人数、70~79分人数、80~89分人数、90分以上人数,输出上述统计结果。
(5)按学号查询。输入学生的学号,查询并输出该学生的各科成绩、平均成绩和总分的名次。
(6)按姓名查询。输入学生的姓名,查询并输出该学生的各科成绩、平均成绩和总分的名次。若有重名的学生,则要求输入学号进行查询。
2. 设计方案
- 数据结构设计(线性表?树?图?要将数据结构设计结果描述清楚)
利用链式线性结构体Stu数据域存储学生信息,学生姓名、班级、年级、学生各科成绩为字符串型,学生编号为整型,指针域存储下一个指针地址。 - 程序功能框架概要设计(菜单、函数调用关系图、主要函数的功能说明等)
(1)功能菜单
(2)函数调用关系图
(3)主要函数功能说明
//链表操作函数
Stu *InitList(int num)初始化链表 ,返回头指针
Stu *NextItem(Stu *plist)传入当前结点,返回下一结点
void AddItem(Stu *plist)尾插结点,传入头结点,遍历到NULL,将尾部的指针域指向尾部新结点
void InitItem(Stu *plist)初始化结点,将plist指向一个空链表,并初始化数据域
Stu *SearchItem(int num,Stu *phead)传入头指针,返回编号为num的结点位置
//功能函数
void Display(Stu *p_head)输出学生信息
void InitSys(Stu *p_head)输入学生信息(初始化系统)
void SaveFile(Stu *p_head)保存文件
int Sort(Stu *p_head,int n_ID,int n_subject)年级排名,传入头指针、学生编号、所要排名的数据类型
int ClassSort(Stu *p_head,int n_ID,int n_subject)班级排名,传入头指针、学生编号、所要排名的数据类型
void Search(Stu *p_head,char *name)搜索学生信息,传入头指针,传入学生姓名
void Del(Stu *p_head,int n_ID)删除表,传入头指针,传入删除学生编号
void Del2(Stu *p_head,int n_ID)删除表,传入头指针,传入删除学生编号
double Strtodouble(char *str)传入符串返回double型函数,若传入其他字符串,则返回-1
double Average(Stu *p_head,int n_ID)计算平均分
void AddList(Stu *p_head)尾部添加新同学
void Change(Stu *p_head)修改
void InputScore(Stu *start , Stu *end , Stu *p_head ,char *subject)单科学习成绩录入,传入开始录入位置和结束录入位置,传入头指针 ,传入需要录入的学科
void ShowItem(Stu *p_list,Stu *p_head)传入指针,输出其学生信息
void Fprint(Stu *p_head)将数据导出到当前路径下的StudentInformation.txt
void Fprint2(Stu *p_head)将数据导出到input.dat
void ClassSortOutput(Stu *p_head,char *str_class,int subject)传入头指针,传入班级,传入学科
void SubjectSort(Stu *p_head,int subject)年级学科排名
void AverageSort(Stu *p_head)平均分排序
void Insert(Stu *p_head)在中间插入一个表
void Statistics(Stu *p_head)统计成绩
void Most(Stu *p_head,int subject)最高分最低分
void Averagesubject(Stu *p_head,int subject)各科平均成绩
- 具体算法详细设计
(1) 遍历并输出列表
传入头指针,传入当前结点,传出下一节点。如此循环调用输出学生信息的函数。
(2) 查找学生
根据姓名查找学生:获取输入的姓名,传入头指针和姓名,定义sign== 0, 在链表中查找是否有姓名与传入的姓名相等的结点,当有此结点时,sign== 1,并且输出该结点学生信息,如果sign==0,则提示“无此学生”。
根据编号查找学生:获取输入的编号,传入头指针和编号,在链表中查找是否有此编号,如果没有此编号,则提示“未找到这个编号”,如果有此编号则输出该编号学生信息。
(3) 删除学生
输入学生编号,如果输入的编号不正确会提示“没有输入正确的编号”,如果输入编号正确,则传入头指针和被删除学生编号,删除函数先释放内存空间,然后删除结点的前后关联,将指针域指向被删除结点的后继结点。
(4) 尾部添加学生
将指针移至链表末尾,调用尾插结点的方法,传入前置结点,遍历到null,将将尾部的指针域指向尾部新结点 ,完成添加,然后对结点写入学生姓名、学生班级等数据。
(5) 修改学生信息
输入被修改学生编号,通过编号搜索结点位置,如果未找到则提示“未找到编号”,如果找到则输出该学生信息,并提示用户选择要修改的信息(A:姓名,B:班级,C:数学成绩,D:语文成绩,E:英语成绩),选择后输入修改内容,将要修改内容插入结点中,如果输入的格式不正确,系统会提示“请输入正确格式”。
(6) 输出年级平均分排序,并将排序内容保存在averagesort.dat文件中。
传入头指针,对平均分进行排序,将数据存储到list数组中,如果排序成功则num加一,最后根据num循环输出list中的学生信息和平均分,并将数据插入到文件averagesort.dat中。
(7) 录入学生单科成绩
输入起始位置编号和终点位置编号,输入要录入的学科(A:数学B:语文C:英语),找到要录入成绩结点编号的开始循环,依次插入要录入的成绩,直到终点。
(8) 将数据导出到文件
打开指定文件,将链表循环将各个结点信息写入打开文件,最后循环结束,关闭文件。
(9) 根据班级对成绩进行排序
输入班级,传入头指针,传入班级,传入学科,循环链表,如果传入班级与链表中的班级相同,则开始排序,将数据保存到list中,最后循环输出排序结果。
(10) 成绩年级排序,并将相关数据写入相关文件。
输入排序学科,传入头指针,传入学科,循环链表,对传入学科进行排序,最后循环输出成绩并写入文件。
(11) 成绩分析
对链表进行循环,用if else 方法进行判断,如果满足条件则相应数值加一,循环完成后输出所有统计数值。对所有各科成绩进行排序,输出最高分和最低分。计算所有科目的平均分,并将它们输出。
(12) 插入学生
在表中插入一个结点,输入想添加的位置,分配新结点,依次输入学生姓名、班级各科成绩等数据,搜索新结点插入位置的前后结点,对后续结点编号加一,将新增结点前一结点指向新结点,将新结点指向后一结点,完成关联。
3 运行截图
- 程序开始时,如果目录下有文件会读取文件并显示菜单,没有就会创建新文件并要求输入学生数据。以下是有文件的情况:
- 输出所有学生信息,显示列出所有学生的所有信息,以下测试20条数据,接下来的操作基于这20条数据进行。
3 增、删、查、改
4 其他操作不做说明
4. 源代码
main.cpp
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <string.h>
#include "list.h"
#include "list.cpp"
#include "menu.h"
#include "menu.cpp"
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
int main(int argc, char** argv)
{
SetConsoleTitle("学生管理系统");
printf("当前运行目录%s\n",argv[0]);
Stu *p_head = NULL;
//文件读取层
FILE *pf;
int i,num;
int sign=0;
//未找到文件需要初始化文件
if((pf = fopen("data.wwy","r")) == NULL)
{
sign=1; //标记
}
fclose(pf);
if(sign == 1)
{
printf("首次使用未找到历史文件,请初始化文件,请输入学生数量(输入0,则不录入任何数据):");
while(scanf("%d",&num)!=1 || num<0)
{
printf("提示:你没有输入正确值,请重新输入:");
fflush(stdin);//while(getchar()!='\n');
}
fflush(stdin);
p_head = InitList(num); //开num个空间的链表
InitSys(p_head); //输入数据域
SaveFile(p_head); //保存文件
printf("欢迎来到学生管理系统\n");
}
else
{
if((pf = fopen("data.wwy","rb")) == NULL)
{
exit(1);
}
//找到文件,将文件读入链表
p_head = InitList(0);
Stu *p_temp = p_head;
while(fread(p_temp,sizeof(struct Stu),1,pf) == 1)
{
AddItem(p_temp);
p_temp = NextItem(p_temp);
}
Del(p_head,p_temp->m_nSign);
printf("提示:读取成功\n欢迎来到学生管理系统\n");
fclose(pf);
}
//工作层
while(1)
{
Mainmenu(p_head);
}
return 0;
}
list.cpp
#include "list.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void InitItem(Stu *plist) //初始化结点,将plist指向一个空链表
{
strcpy(plist->m_nChinese,"0");
strcpy(plist->m_nEnglish,"0");
strcpy(plist->m_nMath,"0");
plist->m_pNext = NULL;
}
void AddItem(Stu *plist) //尾插结点,传入前置结点,遍历到NULL,将尾部的指针域指向尾部新结点 ,完成添加
{
struct Stu *temp = (struct Stu*)malloc(sizeof(struct Stu));
if(temp == NULL)
{
printf("空间分配失败\n");
exit(-1);
}
InitItem(temp);
temp->m_nSign=plist->m_nSign+1;
plist->m_pNext=temp;
temp->m_pNext=NULL;
}
Stu *NextItem(Stu *plist) //传入当前结点,传出下一结点
{
if(plist==NULL)
return NULL;
else
return plist->m_pNext;
}
Stu *InitList(int num) //初始化链表
{
int i;
struct Stu *phead = (struct Stu*)malloc(sizeof(struct Stu)); //创建头结点,编号为0,不存任何数据
struct Stu *temp = phead;
if(phead == NULL)
{
printf("空间分配失败\n");
exit(-1);
}
phead->m_pNext = NULL;
InitItem(phead);
phead->m_nSign = 0;
for(i=1;i<=num;i++)
{
AddItem(temp);
temp=NextItem(temp);
temp->m_nSign = i;
}
return phead;
}
Stu *SearchItem(int num,Stu *phead) //传入头指针,返回编号为num的结点位置
{
struct Stu *temp = phead;
while(temp)
{
if(num == temp->m_nSign)
{
return temp;
}
else
{
temp = NextItem(temp);
}
}
return NULL;
}
list.h
#ifndef _LIST_H_
#define _LIST_H_
//构造数据域和结点
struct Stu
{
//数据域
int m_nSign;
char m_strName[999]; //学生基本信息
char m_strClass[999];
char m_nMath[999]; //各科成绩及对应排名
char m_nChinese[999];
char m_nEnglish[999];
char m_nComputer[999];
//结点
Stu *m_pNext;
};
//链表操作函数
Stu *InitList(int num); //初始化链表 ,返回头指针
Stu *NextItem(Stu *plist);//传入当前结点,返回下一结点
void AddItem(Stu *plist); //尾插结点,传入头结点,遍历到NULL,将尾部的指针域指向尾部新结点
void InitItem(Stu *plist); //初始化结点,将plist指向一个空链表,并初始化数据域
Stu *SearchItem(int num,Stu *phead); //传入头指针,返回编号为num的结点位置
menu.cpp
//函数定义
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "menu.h"
//系统操作函数
void Mainmenu(Stu *p_head) //主菜单显示函数
{
if(p_head == NULL)
{
printf("提示:空间分配错误,请重新打开程序尝试") ;
return;
}
Adminmenu(p_head);
}
void Adminmenu(Stu *p_head) //管理员菜单显示函数
{
while(1)
{
printf("****************************************************\n");
printf(" \t 功能菜单\n");
printf(" \tA:输出所有学生信息 \n \tB:查找学生 \n \tC:删除学生 \n \tD:添加学生 \n \tE:修改学生信息 \n \tF:输出平均分排序 \n");
printf(" \tG:学生单科成绩录入 \n \tH:保存为文件 \n \tI:输出班级单科排名 \n \tJ:输出年级单科排名 \n \tK:成绩分析与统计 \n \tL:插入学生 \n");
printf("****************************************************\n");
printf("请输入你要进行的操作:");
Admininput(p_head);
}
}
void Admininput(Stu *p_head) //管理员菜单输入函数
{
char choice[999]="\0";
gets(choice);
if(strcmp(choice,"A")==0)
{
Display(p_head);
}
else if(strcmp(choice,"B")==0)
{
printf("选择查找方式(A:姓名查找 B:编号查找):");
char cho[999]="\0";
gets(cho);
if(strcmp(cho,"A")==0){
char name[999]="\0";
printf("请输入你要查找学生姓名:");
gets(name);
Search(p_head,name);
}
else if(strcmp(cho,"B")==0){
int n_id;
printf("请输入你要查找的学生编号:");
if(scanf("%d",&n_id)!=1)
{
printf("提示:你没有输入正确编号\n");
fflush(stdin);//while(getchar()!='\n');
}else{
fflush(stdin);
Search(p_head,n_id);
}
}
}
else if(strcmp(choice,"C")==0)
{
int n_ID ;
printf("请输入你要删除的学生的编号:");
if(scanf("%d",&n_ID)!=1)
{
printf("提示:你没有输入正确编号\n");
fflush(stdin);//while(getchar()!='\n');
}
else
{
fflush(stdin);
Del2(p_head,n_ID);
//printf("提示:删除成功\n");
SaveFile(p_head);
}
}
else if(strcmp(choice,"D")==0)
{
int num,num1;
printf("请输入你要添加学生的数量:");
if(scanf("%d",&num)!=1)
{
printf("提示:你没有输入正确数量\n");
fflush(stdin);
}
fflush(stdin);
num1=num;
while(num)
{
printf("提示:你当前正在添加第%d个学生\n",num1-num+1);
AddList(p_head);
num--;
}
SaveFile(p_head);
}
else if(strcmp(choice,"E")==0)
{
Change(p_head);
SaveFile(p_head);
}
else if(strcmp(choice,"F")==0)
{
AverageSort(p_head);
}
else if(strcmp(choice,"G")==0)
{
int start;
int end;
char subject[999];
printf("请输入起始位置编号:");
if(scanf("%d",&start)!=1)
{
printf("提示:你没有输入正确编号\n");
fflush(stdin);
return;
}
fflush(stdin);
Stu *temp1=SearchItem(start , p_head);
if(temp1==NULL || temp1->m_nSign==0)
{
printf("提示:起始位置错误\n");
return;
}
printf("请输入终点位置编号:");
if(scanf("%d",&end)!=1)
{
printf("提示:你没有输入正确编号\n");
fflush(stdin);
return;
}
fflush(stdin