(一)使用单向链表和文件作为基本数据结构,设计一个学生成绩管理程序,管理某学校学生成绩。
(二)数据结构
⑴ 定义单向链表类型StuLink:链表结点包含num、name、sex、score、grade、rank、nxet六个数据项,分别代表学生的学号、姓名、性别、成绩、等级、名次和指向下一个结点的指针,其中:学号、姓名、性别、成绩是输入项,等级、名次是计算项。
(2)定义数据文件student.dat:必须使用数据文件来存储学生信息,每行一个学生信息,各数据项以一个空格隔开。
(三)程序功能
⑴ 设计main函数
A.开始运行程序时,从数据文件中逐行读取学生信息生成学生链表,退出程序时将学生链表中的数据逐行保存到文件。
B.建立一个至少二级菜单系统
一级菜单:包括“1-数据维护,2-数据查询,3-统计分析,4-报表输出,0-退出”几项功能。二级菜单:数据维护包括“数据插入、数据修改、数据删除、返回”几项功能。数据查询包括“学号查询、不及格学生查询、返回”几项功能。统计分析包括“成绩名次计算、成绩频度分析、返回”几项功能。报表输出包括“排序显示学生信息、分页显示学生信息、返回”几项功能。
C.开始运行程序从数据文件中逐行读取学生信息生成学生链表。
D. 退出程序时将学生链表中的数据逐行保存到文件。
(2) 设计ReadFromFile函数:从数据文件中逐行读取学生信息生成学生链表。
(3) 设计SaveToFile函数:先将学生链表按学号升序排序,再将学生链表中的数据逐行保存到数据文件。
(4)设计InsertNode函数:在链表尾插入一个新结点。新结点的学号是链表中最大学号加1,姓名和成绩从键盘输入(注意:成绩必须在[0,100]区间的整数),根据成绩计算等级。注意:插入结点会导致链表中各结点名次的变化。
(5)设计EditNode函数:修改链表中指定学号的结点(学号不能修改,成绩必须在[0,100]区间的整数)。注意:当修改成绩时会导致等级和名次的变化。
(6)设计DeleteNode函数:删除链表中指定学号的结点。注意:删除结点会导致链表中各结点名次的变化。
(7)设计QueryNode函数:查询链表中指定学号的结点,并显示查询结果。
(8)设计QueryLink函数:查询链表中不及格的所有结点,并显示查询结果。
(9)设计RankLink函数:计算链表中每个结点的名次。名次规则:按成绩降序排名,从第1名开始依次排名,若出现并列名次,则名次需要叠加。例如,若出现5个并列第1名,则没有第2名,下一个名次是第6名,依此类推。
(10) 设计AnalysisLink函数:统计并返回各等级人数。等级标准:
A:90及以上 B:80及以上 C:70及以上 D:60及以上 E:60以下
(11)设计SortLink函数:按指定数据项的顺序【学号(升序)】或者【成绩(降序)】对学生链表进行排序。
(12) 设计OutputLink_1函数:按指定数据项的顺序【学号(升序)】或者【成绩(降序)】输出学生成绩表、各等级人数。学生成绩表每行输出一个学生信息(依次为学号、姓名、性别、成绩、等级和名次,各项间以1个空格隔开),各等级人数分行输出。
(13) 设计OutputLink_2函数:分页显示全部学生的信息。分页功能:每页显示10个学生信息,有上一页、下一页、首页和最后一页的翻页功能。
(四)数据约束
⑴ 学号:整型、不能重复、从1开始依次递增、由程序自动计算产生,不能修改。
⑵ 性别:男和女,必须使用枚举类型。
⑶ 成绩:整型,取值范围为0-100。
⑷ 其他:至少有两级菜单。
#include
#include
#include
#include
#include "list.h" //引入自己写的外部头文件
//主函数
void main()
{
int sel;
void Data_maintenance(struct Node* listHead);
void Data_query(struct Node* listHead);
void Statistical_analysis(struct Node* listHead);
void report_output(struct Node* listHead);
void clear();
readInfoFromFile("student.txt",list);
SortLinkByNumber(list);
Analysis(list);
RankLink(list);
system("cls");
while(1)
{
printf("\t\t\t\t\t 学生成绩管理系统 \n");
printf("\t\t\t\t1—数据维护 2—数据查询 \n");
printf("\t\t\t\t3—统计分析 4—报表输出 \n");
printf("\t\t\t\t0—退出系统 \n");
printf("\t\t\t\t请输入您的选项: ");
scanf("%d",&sel);
switch(sel)
{
case 1:
Data_maintenance(list); //调用子菜单一
system("cls");
break;
case 2:
Data_query(list); //调用子菜单二
system("cls");
break;
case 3:
Statistical_analysis(list); //调用子菜单三
system("cls");
break;
case 4:
report_output(list); //调用子菜单四
system("cls");
break;
case 0:
saveInfoToFile("student.txt",list);
printf("\t\t\t\t\t【已退出系统】\n\t\t\t\t");
system("pause");
exit(0);
break;
default:
printf("\t\t\t\t\t数据输入错误,请重新输入");
break;
}
}
saveInfoToFile("student.txt",list);
system("pause");
}
子菜单一
//1-数据维护
void Data_maintenance(struct Node* listHead)
{
int sel,num;
struct student data;
printf("\t\t\t\t1—数据插入 2—数据删除 \n");
printf("\t\t\t\t3—数据修改 4—返回一级菜单 \n");
printf("\t\t\t\t请输入您的选项:");
scanf("%d",&sel);
switch(sel)
{
case 1:
printf("\t\t\t\t请输入要插入的学生的姓名——性别【0表示女生,1表示男生】——成绩 \n");
printf("\t\t\t\t");
scanf("%s%d%d",data.name,&data.sex,&data.score);
insertNodeByHead(listHead,data);
SortLinkByNumber(listHead);
Analysis(listHead);
RankLink(listHead);
clear();
break;
case 2:
SortLinkByNumber(listHead);
printf("\t\t\t\t请输入要删除的信息的学生的学号:");
scanf("%d",&num);
DeleteNode(listHead,&num);
printf("\t\t\t\t删除该学生后的学生信息如下:\n");
Analysis(listHead);
RankLink(listHead);
printf("\t\t\t\t学号——姓名——性别——成绩——等级——名次:\n");
printflist(listHead);
clear();
break;
case 3:
SortLinkByNumber(listHead);
printf("\t\t\t\t请输入要修改信息的学生的学号:");
scanf("%d",&num);
EditNode(listHead,num);
clear();
break;
case 4:
system("cls");
break;
default:
printf("\t\t\t\t输入数据错误,请输入[1,4]区间的整数 \n");
clear();
break;
}
}
子菜单二
//2-数据查询
void Data_query(struct Node* listHead)
{
int sel,num;
struct Node* curNode;
printf("\t\t\t\t1—学号查询 2—不及格学生查询 \n");
printf("\t\t\t\t3—返回一级菜单 \n");
printf("\t\t\t\t请输入您的选项:");
scanf("%d",&sel);
switch(sel)
{
case 1:
printf("\t\t\t\t请输入需要查询学生的学号:");
scanf("%d",&num);
curNode=QueryNodeByNumber(listHead,num);
printfcurNode(curNode);
clear();
break;
case 2:
printf("\t\t\t\t不及格的学生如下 \n");
printf("\t\t\t\t学号 姓名 性别 成绩 等级 名次 \n");
QueryLink(listHead);
clear();
break;
case 3:
system("cls");
break;
default:
printf("\t\t\t\t输入数据错误,请输入[1,4]区间的整数 \n");
clear();
break;
}
}
子菜单三
//3-数据分析
void Statistical_analysis(struct Node* listHead)
{
int sel;
printf("\t\t\t\t1—成绩名次计算 2—成绩频度分析 \n");
printf("\t\t\t\t3—返回一级菜单 \n");
printf("\t\t\t\t请输入您的选项:");
scanf("%d",&sel);
switch(sel)
{
case 1:
SortLinkByNumber(listHead);
Analysis(listHead);
RankLink(listHead);
printfscore(listHead);
clear();
break;
case 2:
AnalysisLink(listHead);
clear();
break;
case 3:
system("cls");
break;
default:
printf("\t\t\t\t输入数据错误,请输入[1,3]区间的整数 \n");
clear();
break;
}
}
子菜单四
//4-报表输出
void report_output(struct Node* listHead)
{
int sel,sel2,i;
struct Node* curNode;
printf("\t\t\t\t1—排序显示学生信息 2—分页显示学生信息 \n");
printf("\t\t\t\t3—返回一级菜单 \n");
printf("\t\t\t\t请输入您的选项:");
scanf("%d",&sel);
switch(sel)
{
case 1:
printf("\t\t\t\t【1】按学号升序排列学生信息\n");
printf("\t\t\t\t【2】按成绩降序排列学生信息\n");
printf("\t\t\t\t请输入您的选项:");
scanf("%d",&sel2);
OutputLink_1(listHead,sel2);
clear();
break;
case 2:
system("cls");
printf("\t\t\t\t学号 姓名 成绩 性别 等级 名次 \n");
i=Outprintften(listHead,1); //i=11
//curNode=QueryNodeByNumber(listHead,1); //找到第一个学生
printf("\t\t\t\ta键上一页,d键下一页,w键首页,s键尾页, Esc返回一级菜单 \n");
Outprintf_2(listHead,i);
break;
clear();
case 3:
system("cls");
break;
default:
printf("\t\t\t\t输入数据错误,请输入[1,3]区间的整数 \n");
clear();
break;
}
}
清理屏幕
//清理屏幕
void clear()
{
printf("\t\t\t\t");
system("pause");
system("cls");
}
//数据结构体
enum Sex //以枚举的方式定义学生性别
{
girl,
boy
};
struct student
{
char name[20];
enum Sex sex;
int score;
int num;
char grade;
int rank;
};
struct Node
{
struct student data;
struct Node* next;
};
//函数
//1-设计ReadFromFile()函数,从数据文件中逐行读取文件信息生成链表
void ReadFromFile(char *fileName, struct Node* listHeadNode)
{
struct student tempData;
FILE *fp = fopen(fileName, "r");
if (fp == NULL)
{
fp = fopen(fileName, "w");
}
while (fscanf(fp,"%s%d%d%%s%d%d", tempData.name, &tempData.sex, &tempData.score, tempData.grade, &tempData.num,&tempData.rank) != EOF)
{
insertNodeByHead(listHeadNode, tempData);
memset(&tempData, 0, sizeof(tempData));
}
fclose(fp);
}
//2-设计SaveToFile函数:先将学生链表按学号升序排序,再将学生链表中的数据逐行保存到数据文件。
void SaveToFile(char *fileName, struct Node* listHeadNode)
{
FILE *fp ;
struct Node* pMove = listHeadNode->next;
if((fp=fopen("student.txt","w"))==NULL)
{
printf("无法正常打开文件 \n");
exit(0);
}
while (pMove!=NULL)
{
fprintf(fp,"%d %s %d %c %d \n", pMove->data.num, pMove->data.name, pMove->data.score,pMove->data.grade,pMove->data.rank);
pMove = pMove->next;
}
fclose(fp);
}
//3-设计InsertNode()函数在链表尾插入一个新结点,新结点的学号是链表中最大学号加1,姓名和成绩从键盘输入(注意:成绩必须在[0,100]区间的整数),根据成绩计算等级。
void insertNodeByHead(struct Node* listHeadNode, struct student data)
{
//想不清楚,那就去画清楚
struct Node* newNode = createNode(data);
newNode->next = listHeadNode->next;
listHeadNode->next = newNode;
}
//4-设计EditNode函数:修改链表中指定学号的结点
void EditNode(struct Node* listHead,int num)
{
struct Node* curNode=QueryNodeByNumber(listHead,num);
printf("\t\t\t\t请输入修改后学生的——姓名——性别——成绩 \n");
printf("\t\t\t\t");
scanf("%s%d%d",curNode->data.name,&curNode->data.sex,&curNode->data.score);
SortLinkByNumber(listHead);
Analysis(listHead);
RankLink(listHead);
printf("\t\t\t\t学号——姓名——性别——成绩——等级——名次:\n");
printflist(listHead);
}
//5-设计DeleteNode函数:删除链表中指定学号的结点
void DeleteNode(struct Node* listHead,int *num)
{
struct Node* posNode=listHead->next;
struct Node* posNodeFront=listHead;
if(posNode==NULL)
{
printf("无数据可以删除 \n");
return;
}
else
{
while(posNode->data.num!=*num)
{
posNodeFront=posNode;
posNode=posNode->next;
if(posNode==NULL)
{
printf("未找到该学号学生,无数据可删除 \n");
return;
}
}
posNodeFront->next=posNode->next;
free(posNode);
}
}
//6-设计QueryNode函数,查询链表中指定学号的结点,并显示查询结果
struct Node* QueryNodeByNumber(struct Node* listHead,int num)
{
struct Node* pMove=listHead->next;
if(pMove==NULL)
return pMove;
else
{
while(pMove->data.num!=num)
{
pMove=pMove->next;
if(pMove==NULL)
break;
}
}
return pMove;
}
//7-设计QueryLink函数:查询链表中不及格的所有结点,并显示查询结果。
void QueryLink(struct Node* listHead)
{
struct Node* curNode=listHead->next;
while(curNode!=NULL)
{
if(curNode->data.score<60)
{
switch(curNode->data.sex)
{
case boy:
printf("\t\t\t\t%d %s 男 %d %c %d \n",curNode->data.num,curNode->data.name,curNode->data.score,curNode->data.grade,curNode->data.rank);
break;
case girl:
printf("\t\t\t\t%d %s 女 %d %c %d \n",curNode->data.num,curNode->data.name,curNode->data.score,curNode->data.grade,curNode->data.rank);
break;
}
}
curNode=curNode->next;
}
}
//8-设计RankLink函数:计算链表中每个结点的名次
void RankLink(struct Node* listHead)
{
struct Node* list1=listHead->next,*list2=list1;
list1->data.rank=1;
while(list1!=NULL)
{
while(list2!=NULL)
{
if(list1->data.score<list2->data.score)
{
list1->data.rank++;
}
list2=list2->next;
}
list1=list1->next;
if(list1!=NULL)
{
list1->data.rank=1;
}
list2=listHead->next;
}
}
//9-设计AnalysisLink函数:统计并返回各等级人数
void AnalysisLink(struct Node* listHead)
{
struct Node* list=listHead->next;
int res[6]={0,0,0,0,0,0}; //分段计数器数组
while(list!=NULL)
{
if(list->data.score<60)
{
res[0]++;
list->data.grade='E';
}
else if(list->data.score!=100)
{
if(list->data.score>=60&&list->data.score<70)
{
list->data.grade='D';
}
if(list->data.score>=70&&list->data.score<80)
{
list->data.grade='C';
}
if(list->data.score>=80&&list->data.score<90)
{
list->data.grade='B';
}
if(list->data.score>=90&&list->data.score<100)
{
list->data.grade='A';
}
res[(list->data.score/10)-5]++;
}
else
{
res[4]++;
list->data.grade='A';
}
list=list->next;
}
printf("\t\t\t\t\t 学生等级统计结果: \n");
printf("\t\t不及格 [60,70) [70,80) [80,90) [90,100]\n");
printf("\t\t%d人 %d人 %d人 %d人 %d人 \n",res[0],res[1],res[2],res[3],res[4]);
}
//10-设计SortLink函数:按指定数据项的顺序【学号(升序)】或者【成绩(降序)】对学生链表进行排序。
void SortLinkByNumber(struct Node* listHead)
{
struct Node* list1=listHead->next;
int num=1;
while(list1!=NULL)
{
list1->data.num=num;
list1=list1->next;
num++;
}
}
void SortLinkByscore(struct Node* listHead)
{
struct Node* list1,*list2;
struct student tempdata;
for(list1=listHead->next;list1!=NULL;list1=list1->next)
for(list2=list1->next;list2!=NULL;list2=list2->next)
if(list1->data.score<list2->data.score)
tempdata=list2->data,list2->data=list1->data,list1->data=tempdata;
}
//11-设计OutputLink_1函数:按指定数据项的顺序【学号(升序)】或者【成绩(降序)】输出学生信息
void OutputLink_1(struct Node* listHead,int sel)
{
switch(sel)
{
//按学号升序输出学生信息
case 1:
SortLinkByNumber(listHead);
printf("\t\t\t\t学号 姓名 性别 成绩 等级 名次 \n");
printflist(listHead);
break;
case 2:
//按成绩降序输出学生信息
SortLinkByNumber(listHead);
SortLinkByscore(listHead);
printf("\t\t\t\t学号 姓名 性别 成绩 等级 名次 \n");
printflist(listHead);
break;
default:
printf("\t\t\t\t输入数据错误,请输入1或2");
break;
}
}
//12-设计OutputLink_2函数:分页显示全部学生的信息,函数包含首页,上一页,下一页,尾页功能,每页显示10人。
void Outprintf_2(struct Node* listHead,int i)
{
char decision=getch();
int total=totalnum(listHead);
struct Node* curNode;
switch(decision)
{
case 'a': //a键上一页
if(i<=11)
{
printf("\t\t\t\t已是第一页 \n");
printf("\t\t\t\ta键上一页,d键下一页,w键首页,s键尾页, Esc返回一级菜单 \n");
Outprintf_2(listHead,i);
system("pause");
}
else
{
system("cls");
printf("\t\t\t\t学号 姓名 成绩 性别 等级 名次 \n");
i=Outprintften(listHead,i-20);
printf("\t\t\t\ta键上一页,d键下一页,w键首页,s键尾页, Esc返回一级菜单 \n");
Outprintf_2(listHead,i);
}
break;
case 'd': //d键下一页
if(i>=((total/10)*10+(total%10))+1)
{
i=(total/10)*10+11;
printf("\t\t\t\t已到尾页 \n");
printf("\t\t\t\ta键上一页,d键下一页,w键首页,s键尾页, Esc返回一级菜单 \n");
Outprintf_2(listHead,i);
}
else
{
system("cls");
printf("\t\t\t\t学号 姓名 成绩 性别 等级 名次 \n");
i=Outprintften(listHead,i);
printf("\t\t\t\ta键上一页,d键下一页,w键首页,s键尾页, Esc返回一级菜单 \n");
Outprintf_2(listHead,i);
}
break;
case 'w': //w键返回首页
if(i<=11)
{
printf("\t\t\t\t已是第一页 \n");
Outprintf_2(listHead,i);
system("pause");
}
else
{
system("cls");
printf("\t\t\t\t学号 姓名 成绩 性别 等级 名次 \n");
i=Outprintften(listHead,1); //i=11
printf("\t\t\t\ta键上一页,d键下一页,w键首页,s键尾页, Esc返回一级菜单 \n");
Outprintf_2(listHead,i);
system("pause");
}
break;
case 's': //s键去到尾页
system("cls");
i=(total/10)*10+1;
system("cls");
printf("\t\t\t\t学号 姓名 成绩 性别 等级 名次 \n");
i=Outprintften(listHead,i); //i
printf("\t\t\t\ta键上一页,d键下一页,w键首页,s键尾页, Esc返回一级菜单 \n");
i=(total/10)*10+11;
Outprintf_2(listHead,i);
printf("\t\t\t\t\t\t【请按回车键返回】 \n\t\t\t\t\t\t");
system("pause");
system("cls");
break;
case '27':
system("cls");
break;
}
}
//头插法
void insertNodeByHead(struct Node* listHeadNode, struct student data)
{
//想不清楚,那就去画清楚
struct Node* newNode = createNode(data);
newNode->next = listHeadNode->next;
listHeadNode->next = newNode;
}
//创建数据结点
struct Node* createNode(struct student data)
{
//有表头链表:第一个结点不存储数据
//无表头链表:第一个结点存储数据
//1.产生一个结构体变量
struct Node* newNode = (struct Node*)malloc(sizeof(struct Node));
//2.初始化一个变量
newNode->data = data;
newNode->next = NULL;
return newNode;
}
//计算学号
void SortLinkByNumber(struct Node* listHead)
{
struct Node* list1=listHead->next;
int num=1;
while(list1!=NULL)
{
list1->data.num=num;
list1=list1->next;
num++;
}
}
//统计各等级人数等级
void Analysis(struct Node* listHead)
{
struct Node* list=listHead->next;
int res[6]={0,0,0,0,0,0}; //分段计数器数组
while(list!=NULL)
{
if(list->data.score<60)
{
res[0]++;
list->data.grade='E';
}
else if(list->data.score!=100)
{
if(list->data.score>=60&&list->data.score<70)
{
list->data.grade='D';
}
if(list->data.score>=70&&list->data.score<80)
{
list->data.grade='C';
}
if(list->data.score>=80&&list->data.score<90)
{
list->data.grade='B';
}
if(list->data.score>=90&&list->data.score<100)
{
list->data.grade='A';
}
res[(list->data.score/10)-5]++;
}
else
{
res[4]++;
list->data.grade='A';
}
list=list->next;
}
}
//计算学生名次
void RankLink(struct Node* listHead)
{
struct Node* list1=listHead->next,*list2=list1;
list1->data.rank=1;
while(list1!=NULL)
{
while(list2!=NULL)
{
if(list1->data.score<list2->data.score)
{
list1->data.rank++;
}
list2=list2->next;
}
list1=list1->next;
if(list1!=NULL)
{
list1->data.rank=1;
}
list2=listHead->next;
}
}
//打印链表信息
void printflist(struct Node* listHead)
{
struct Node* curNode=listHead->next;
while(curNode!=NULL)
{
switch(curNode->data.sex)
{
case boy:
printf("\t\t\t\t%d %s 男 %d %c %d \n",curNode->data.num,curNode->data.name,curNode->data.score,curNode->data.grade,curNode->data.rank);
break;
case girl:
printf("\t\t\t\t%d %s 女 %d %c %d \n",curNode->data.num,curNode->data.name,curNode->data.score,curNode->data.grade,curNode->data.rank);
break;
}
curNode=curNode->next;
}
}
//计算总人数
int totalnum(struct Node* listHead)
{
struct Node* list=listHead->next;
int count=0;
while(list!=NULL)
{
count++;
list=list->next;
}
return count;
}
//打印10个人
int Outprintften(struct Node* listHead,int i)
{
struct Node* curNode=QueryNodeByNumber(listHead,i);
while(curNode!=NULL)
{
while(curNode!=NULL&&curNode->data.num<i+10)
{
printfcurNode(curNode);
curNode=curNode->next;
}
if(curNode!=NULL&&curNode->data.num>=i+10)
return curNode->data.num;
else return totalnum(listHead)+1;
}
}
//打印查找到的结点的学生信息
void printfcurNode(struct Node* curNode)
{
switch(curNode->data.sex)
{
case boy:
printf("\t\t\t\t%d %s 男 %d %c %d \n",curNode->data.num,curNode->data.name,curNode->data.score,curNode->data.grade,curNode->data.rank);
break;
case girl:
printf("\t\t\t\t%d %s 女 %d %c %d \n",curNode->data.num,curNode->data.name,curNode->data.score,curNode->data.grade,curNode->data.rank);
break;
}
}
//输出排名后学生的信息
void printfscore(struct Node* listHead)
{
struct Node* curNode=listHead->next;
printf("\t\t\t\t学号 姓名 性别 成绩 等级 名次如下 \n");
while(curNode!=NULL)
{
switch(curNode->data.sex)
{
case boy:
printf("\t\t\t\t%d %s 男 %d %c %d \n",curNode->data.num,curNode->data.name,curNode->data.score,curNode->data.grade,curNode->data.rank);
break;
case girl:
printf("\t\t\t\t%d %s 女 %d %c %d \n",curNode->data.num,curNode->data.name,curNode->data.score,curNode->data.grade,curNode->data.rank);
break;
}
curNode=curNode->next;
}
}
struct Node* createList()
{
//有表头链表:第一个结点不存储数据
//无表头链表:第一个结点存储数据
//1.产生一个结构体变量
struct Node* listHeadNode = (struct Node*)malloc(sizeof(struct Node));
//2.初始化一个变量
listHeadNode->next = NULL;
return listHeadNode;
}
大一下学期期末大作业,不足之处请各路大佬多多指教