结束了大一上C语言的学习,脱离了被学生成绩管理系统支配的恐惧,开始来学习 万恶的 数据结构。
依稀记得当时为了做这个实验,我早早地吃完晚饭,守在电脑前,打开了 CodeBlocks、百度搜索(懂的都懂!!! )、QQ和微信(哈哈,信我,真的只是为了与外界取得联系!!! ),面前还摆着C语言课本和数结构课本, 真的是正襟危坐,紧张刺激。
这里说明一下,去年因为疫情的影响,全部在家上网课,而且数据结构实验课是在晚上。
等等,晚上?这不是变相让我们决战到天亮吗???
果然,老师诚不负我。曾天真地以为可以永远告别学生成绩管理系统了,好家伙,它又给我回来了!!!(原地死亡)
实验要求如下:
学期结束,辅导员需要收集两个班级的同学的C语言课程成绩。请你为辅导员做一个成绩录入统计,帮助辅导员更好地工作。(请问你分钱给我吗?没有凭什么将你的快乐建立在我的痛苦之上? )
(1)成绩信息录入——线性表的建立与遍历
操作1:初始化空表
操作2:按成绩降序插入所有同学的学号、班级信息和成绩到各自班级链表
操作3:查找/删除,若对应班级链表中无该同学信息,则输出无法找到
操作4:信息输出,线性表的遍历
(2)信息汇总
信息统计:将同学们的成绩合并到总表
(3)建表翻转
将总表中信息翻转到新表中
算了,还是写吧。
但请你记住,我不是在帮你,我只是想改变世界!!!
下面奉上鄙人的拙劣的代码:
#include
#include
//定义学生信息节点
typedef struct node{
int ID_num; //学号
int Class_num; //班级
int C_score; //分数
struct node *next;
}STUDENTNODE;
//建立学生信息节点,返回指向该节点的指针
STUDENTNODE* StudentListNodeCreat(int Class_num, int ID_num, int C_score){
STUDENTNODE *p;
p = (STUDENTNODE *)malloc(sizeof(STUDENTNODE)); //给新建节点分配存储空间
if(!p) exit(0);
p->ID_num = ID_num;
p->Class_num = Class_num;
p->C_score = C_score;
p->next = NULL; //指针初始化为NULL
return p;
}
//在已有的成绩降序链表中插入新建学生信息节点,并保持链表成绩仍为降序
STUDENTNODE* Insert_Descending(STUDENTNODE *head, STUDENTNODE *node){
STUDENTNODE *p, *q;
p = head;
q = node;
while(p->next){
//链表下一节点存在
if((p->next->C_score) > q->C_score){
//若下一节点的成绩大于插入节点的成绩
p = p->next; //继续向下搜索
}
else{
q->next = p->next; //若下一节点的成绩小于或等于插入节点的成绩
p->next = q;
break;
}
}
if(!(p->next)){
q->next = p->next; //搜索至链表末端时,将节点插入末端
p->next = q;
}
return head;
}
//根据学号搜索学生信息,若有,则输出成绩,若没有,输出没有该学生信息
void SearchByID_num(STUDENTNODE *head, int ID_num){
STUDENTNODE *p;
p = head;
while(p->next){
//下一节点存在
if((p->next->ID_num) != ID_num){
//若下一节点的学号不等于搜索的学号
p = p->next; //继续向下搜索
}
else{
printf("It is { Class_number: %d, ID_number: %d, C_score: %3d }\n", p->next->Class_num, p->next->ID_num, p->next->C_score); //匹配到相应学号时
break;
}
}
if(!(p->next)){
printf("This class has no such student as ID_number = %d\n", ID_num); //搜索至链表末端时,返回没有相应学生信息
}
}
//根据学号删除学生相关信息
STUDENTNODE* DeleteByID_num(STUDENTNODE *head, int ID_num){
STUDENTNODE *p, *q;
p = head;
while(p->next){
//下一节点存在
if((p->next->ID_num) != ID_num){
//若下一节点的学号不等于搜索的学号
p = p->next; //继续向下搜索
}
else{
q = p->next; //匹配到相应学号时
p->next = q->next; //将p的指针指向下一节点存放的指针所指向的地址
free(q); //释放内存
printf("Success!\n");
break;
}
}
if(!(p->next)){
printf("This class has no such student as ID_number = %d\n", ID_num); //搜索至链表末端时,返回没有相应学生信息
}
return head;
}
//复制参数节点
STUDENTNODE* NodeCopy(STUDENTNODE *node){
STUDENTNODE *newnode;
newnode = (STUDENTNODE *)malloc(sizeof(STUDENTNODE)); //为新节点分配空间复制各部分的值,指针初始化为NULL
newnode->Class_num = node->Class_num; //复制各部分的值指针初始化为NULL
newnode->ID_num = node->ID_num;
newnode->C_score = node->C_score;
newnode->next = NULL; //指针初始化为NULL
return newnode;
}
//将两个班级的成绩表合并为一个新的总表,表中成绩仍按降序处理,同时不破坏原有分表
STUDENTNODE* MergeStudentList(STUDENTNODE* heads[], STUDENTNODE *MergeList){
//参数为待合并分表和合并总表头结点的指针
STUDENTNODE *p, *q, *r, *copynode;
p = heads[0]->next;
q = heads[1]->next;
r = MergeList;
while(p && q){
//当两个指针指向的节点同时存在时,比较两节点的成绩
if((p->C_score) >= (q->C_score)){
//p指针指向节点的成绩大于或等于q指针指向节点的成绩
copynode = NodeCopy(p); //复制p指针指向的节点,该节点为满足条件的待插入节点
r->next = copynode;
r = r->next;
p = p->next;
}
else{
copynode = NodeCopy(q); //复制q指针指向的节点,该节点为满足条件的待插入节点
r->next = copynode;
r = r->next;
q = q->next;
}
}
while(p){
//当p指针指向节点存在,即分表还未遍历时,依次插入所有节点
copynode = NodeCopy(p);
r->next = copynode;
r = r->next;
p = p->next;
}
while(q){
//当q指针指向节点存在,即分表还未遍历时,依次插入所有节点
copynode = NodeCopy(q);
r->next = copynode;
r = r->next;
q = q->next;
}
return MergeList;
}
//将总表逆序排列,即表中成绩按升序处理,同时不破坏原有总表
STUDENTNODE* ReverseStudentList(STUDENTNODE *MergeList,STUDENTNODE *ReverseList){
//参数为待逆序总表和逆序总表头结点的指针
STUDENTNODE *p, *q, *newnode;
p = MergeList->next;
q = ReverseList;
while(p){
//依次遍历并复制每一个节点,不断将复制形成的节点插入到头结点与第一个节点之间,最终达到逆序的效果
newnode = NodeCopy(p); //复制当前节点
newnode->next = q->next; //将复制形成的节点插入头结点与第一个节点之间
q->next = newnode;
p = p->next;
}
return ReverseList;
}
//打印单个学生节点信息
void PrintStudentNode(STUDENTNODE *node){
STUDENTNODE *p;
p = node;
printf("{ Class_number: %d, ID_number: %d, C_score: %3d}\n", p->Class_num, p->ID_num, p->C_score);
}
//打印整个学生信息表
void PrintStudentList(STUDENTNODE *head){
STUDENTNODE *p;
p = head->next;
while(p){
printf("{ Class_number: %d, ID_number: %d, C_score: %3d }", p->Class_num, p->ID_num, p->C_score);
p = p->next;
if(p){
printf("->\n"); //存在后继节点,则以"->"来表示
}
}
printf("\nThat's all\n");
}
int main()
{
int n_student; //学生个数
char order; //用户选择
int tempClass;
int tempID;
int tempScore;
STUDENTNODE* tempNode;
STUDENTNODE* MergeList; //降序总表头指针
STUDENTNODE* ReverseList; //升序总表头指针
STUDENTNODE* Class[2] = {
StudentListNodeCreat(0,0,0),StudentListNodeCreat(1,0,0)}; //为两个班级分表创建头结点
printf("\n ################################################################ \n");
printf("1.Insert_Descending 2.Search by ID_number 3.Delete by ID_number 4.Merge 5.Reverse 6.Print q.Quit\n");
printf("\n ################################################################ \n");
printf("Input your order: ");
while(scanf(" %c", &order)){
//检查是否读取到有效值
switch (order){
//查看用户指令
case '1':
printf("How many student do you want to input? ");
while(getchar() != '\n'){
//清除缓冲区中的所有字符
continue;
}
while(scanf("%d", &n_student)){
//检查是否读取到有效值
while(n_student != 0){
printf("Input the data format as Class_number,ID_number,C_score: ");
while(getchar() != '\n'){
//清除缓冲区中的所有字符
continue;
}
while((scanf("%d,%d,%d", &tempClass, &tempID, &tempScore) != 3)){
//检查是否读取到有效值
while(getchar() != '\n'){
//清除缓冲区中的所有字符
continue;
}
printf("Error!Please input again!\n"); //提示错误并要求重新输入
printf("Input the data format as Class_number,ID_number,C_score: ");
}
tempNode = StudentListNodeCreat(tempClass, tempID, tempScore); //新建学生信息节点
Class[tempClass] = Insert_Descending(Class[tempClass], tempNode); //将新建节点按降序插入班级表中
printf("This is the node you have inserted: ");
PrintStudentNode(tempNode); //输出插入的学生节点信息
n_student--;
}
break;
case '2':
printf("Input the data format as:Class_number,ID_number: ");
while((scanf("%d,%d", &tempClass, &tempID) != 2)){
//检查是否读取到有效值
while(getchar() != '\n'){
//清除缓冲区中的所有字符
continue;
}
printf("Error!Please input again!\n"); //提示错误并要求重新输入
printf("Input the data format as:Class_number,ID_number: ");
}
SearchByID_num(Class[tempClass], tempID);
break;
case '3':
printf("Input the data format as:Class_number,ID_number: ");
while((scanf("%d,%d", &tempClass, &tempID) != 2)){
//检查是否读取到有效值
while(getchar() != '\n'){
//清除缓冲区中的所有字符
continue;
}
printf("Error!Please input again!\n"); //提示错误并要求重新输入
printf("Input the data format as:Class_number,ID_number: ");
}
Class[tempClass] = DeleteByID_num(Class[tempClass], tempID);
break;
case '4':
MergeList = StudentListNodeCreat(-1,0,0); //为降序总表创建一个头结点
MergeList = MergeStudentList(Class,MergeList);
printf("The list merged:\n");
PrintStudentList(MergeList);
break;
case '5':
ReverseList = StudentListNodeCreat(-1,0,0); //为升序总表创建一个头结点
ReverseList = ReverseStudentList(MergeList,ReverseList);
printf("The list reversed:\n");
PrintStudentList(ReverseList);
break;
case '6':
printf("\nClass 0:\n");
PrintStudentList(Class[0]);
printf("\n");
printf("\nClass 1:\n");
PrintStudentList(Class[1]);
break;
case 'q':
exit(0);
default:
printf("Error!Please input again!\n"); //若用户输入选项以外的其他字符,提示输入错误并要求重新输入
break;
}
printf("\n ################################################################ \n");
printf("1.Insert_Descending 2.Search by ID_number 3.Delete by ID_number 4.Merge 5.Reverse 6.Print q.Quit\n");
printf("\n ################################################################ \n");
printf("Input your order: ");
}
}
return 0;
}
除了满足基本要求外,鄙人还作贱自己添加了几个小小的功能:
1、程序只会读取输入的第一个字符作为指令,且程序在读到除以上字符外的其他字符时,都会提示用户重新输入,直至输入正确指令。
2、程序在读取输入时会通过scanf函数的返回值判断输入是否达到要求,若返回值错误,则会提示用户重新输入,直至输入格式完全正确,来提高程序的健壮性。
3、程序在每次读取输入之后都会清空缓冲区中的字符,避免存留字符对下一次的读取造成严重影响,可进一步防止程序崩溃。
4、在合并总表及翻转总表时,采用复制节点的方式,保证原表节点的信息不变,因此在合并或翻转后打印原表,也不会出现存放数据错误的现象。
5、在插入节点之后都会输出插入的节点信息,便于用户纠错并删除错误节点。