《数据结构与算法》附加作业一报告:成绩管理系统
一、目的和要求(需求分析)
建立数据库,并对信息进行插入、修改、删除与查找。
具体要求:1. 提供用户界面提示,方便操作
2. 登记学生基本信息及各门功课成绩
3. 修改学生基本信息及各门功课成绩
4. 删除某学生或某班所有学生的基本信息或各门功课成绩
5. 查找某学生的基本信息或全部成绩信息
二、程序设计的基本思想,原理和算法描述
1. 数据结构:建立一个带有头结点和尾结点的双向链表。
原理:该链表需要经常访问某个节点的前驱和后继,故使用双向链表。
2. 利用for循环语句完成链表元素的输入、删除、修改、查找。
原理:用for循环从前往后遍历链表,找到要插入、删除或修改元素的结点位置,对其进行相关操作。
3. 输入/输出设计:通过用户界面提示,引导用户管理信息,完成操作。
4. 符号名说明:变量名太多,使用英文单词直译方便理解。
三、调试和运行程序过程中产生的问题及采取的措施
1. 问题:一开始觉得无从下手,有好多操作需要完成。
采取措施:结合平时生活中数据管理需要进行的操作,在草稿纸上写下自己的程序需要满足的操作要求。在确定要求后,开始动手,更有方向与目标。
2. 问题:程序中变量名太多且程序代码很长,写代码时十分混乱。
采取措施:采用了分模块设计的思路,先将主函数内需要的操作码好,再去定义调用的各函数。
3. 问题:没有考虑到原本数据不存在的特殊情况。
采取措施:改善代码,通过在代码中加入异常检测解决了该问题。
四、源程序及注释
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//将所有链表都定义为双向链表,方便操作
//科目链表ADT
typedef struct subNode{
//科目链表节点
char name[10]; // 科目名称
int score; // 成绩
struct subNode *next; // 下一个节点
struct subNode *previous; // 前一个节点
} *subPosition;
typedef struct {
//科目链表结构体
subPosition head;
subPosition tail;
} subList;
subList createSubList();
int subListEmpty(subList l);
void insertSub(subPosition p, subList *l, char *name, int score);
void delSub(subPosition p, subList *l);
subPosition findSub(subList l, char *name);
void clearSubList(subList *l);
void printSubList(subList l);
void destroySubList(subList *l);
//学生链表ADT
typedef struct stuNode {
//学生链表节点
long id; // 学号
char name[10]; //姓名
int sex; //性别,1为男,0为女
int age; //年龄
long telephone; //电话
subList subjects; //课程链表
struct stuNode *next; // 下一个节点
struct stuNode *previous; // 前一个节点
} *stuPosition;
typedef struct {
//学生链表结构体
stuPosition head;
stuPosition tail;
} stuList;
stuList createStuList();
int stuListEmpty(stuList l);
void insertStu(stuPosition p, stuList *l, long id, char *name, int sex, int age, long telephone);
void delStu(stuPosition p, stuList *l);
stuPosition findStu(stuList l, long id);
void clearStuList(stuList *l);
void printStuList(stuList l);
void destroyStuList(stuList *l);
//班级链表ADT
typedef struct classNode {
//班级链表节点
int classId; // 班级编号
stuList students; // 学生链表
struct classNode *next; // 下一个节点
struct classNode *previous; // 前一个节点
} *classPosition;
typedef struct {
// 班级链表结构体
classPosition head;
classPosition tail;
} classList;
classList createClassList();
int classListEmpty(classList l);
void insertClass(classPosition p, classList *l, int classId);
void delClass(classPosition p, classList *l);
classPosition findClass(classList l, int classId);
void clearClassList(classList *l);
void printClassList(classList l);
void destroyClassList(classList *l);
// 用户界面
void showMainInterface();
int main()
{
classList classes = createClassList(); // 初始化班级链表
int flag = 1; // 判断用户是否选择退出程序。若退出,值为0;否则为1
showMainInterface(); //显示主菜单
while(flag)
{
printf("\n请输入操作代号:");
int optNo; // 操作代号
scanf("%d", &optNo);
switch (optNo)
{ //根据用户输入的操作代号选择对应的操作
case 1:
{ //查看所有班级
if (classListEmpty(classes)) printf("\n当前还未添加任何班级!\n");
else printClassList(classes);
break;
}
case 2:
{ //查看某个班级内的所有学生
printf("请输入班级编号:");
int classId;
scanf("%d", &classId);
classPosition class = findClass(classes, classId);
if (!class) printf("\n未找到该班级!\n");
else if (stuListEmpty(class->students)) printf("\n该班级内还未添加任何学生!\n");
else printStuList(class->students);
break;
}
case 3:
{ //查看某个学生的基本信息
printf("请输入学生所在的班级编号和学号,中间用空格分隔:");
int classId;
long stuId;
scanf("%d%ld", &classId, &stuId);
classPosition class = findClass(classes, classId);
if (!class) printf("\n未找到该班级!\n");
else
{
stuPosition stu = findStu(class->students, stuId);
if (!stu) printf("\n未找到该学生!\n");
else
{
printf("\n学号:%ld", stu->id);
printf(" 姓名:%s", stu->name);
printf(" 性别:%s", stu->sex == 1 ? "male" : "female");
printf(" 年龄:%d", stu->age);
printf(" 电话:%ld\n", stu->telephone);
}
}
break;
}
case 4:
{ //查看某个学生的所有成绩
printf("请输入学生所在的班级和学号,中间用空格分隔:");
int classId;
long stuId;
scanf("%d%ld", &classId, &stuId);
classPosition class = findClass(classes, classId);
if (!class) printf("\n未找到该班级!\n");
else
{
stuPosition stu = findStu(class->students, stuId);
if (!stu) printf("\n未找到该学生!\n");
else if (subListEmpty(stu->subjects)) printf("\n该学生还未添加任何成绩!\n");
else printSubList(stu->subjects);
}
break;
}
case 5:
{ //插入新班级
printf("请输入班级编号:");
int classId;
scanf("%d", &classId);
classPosition class = findClass(classes, classId);
if (class) printf("\n当前班级已存在!\n");
else insertClass(classes.tail, &classes, classId);
break;
}
case 6:
{ //插入新学生
printf("请输入学生所在的班级编号、学号、姓名(英文)、性别(male/female)、年龄和电话,中间用空格分隔:\n");
int classId, age;
long stuId, telephone;
char name[10], sex[10];
scanf("%d%ld%s%s%d%ld", &classId, &stuId, name, sex, &age, &telephone);
classPosition class = findClass(classes, classId);
if (!class) printf("\n该班级不存在!\n");
else
{
stuPosition stu = findStu(class->students, stuId);
if (stu) printf("\n当前学生已存在!\n");
else insertStu(class->students.tail, &class->students,stuId, name, !strcmp(sex, "male") ? 1 : 0, age, telephone);
}
break;
}
case 7:
{ //插入新成绩
printf("请输入成绩所属学生的班级编号、学号、成绩对应的科目名称(英文)和成绩,中间用空格分隔:\n");
int classId, score;
long stuId;
char subName[10];
scanf("%d%ld%s%d", &classId, &stuId, subName, &score);
classPosition class = findClass(classes, classId);
if (!class) printf("\n该班级不存在!\n");
else
{
stuPosition stu = findStu(class->students, stuId);
if (!stu) printf("\n该学号不存在!\n");
else
{
subPosition sub = findSub(stu->subjects, subName);
if (sub) printf("\n当前成绩已存在!\n");
else insertSub(stu->subjects.tail, &stu->subjects, subName, score);
}
}
break;
}
case 8:
{ //删除某个班级
printf("请输入要删除的班级编号:");
int classId;
scanf("%d", &classId);
classPosition class = findClass(classes, classId);
if (!class) printf("\n该班级不存在!\n");
else delClass(class, &classes);
break;
}
case 9:
{ //删除所有班级
clearClassList(&classes);
break;
}
case 10:
{ //删除某个学生
printf("请输入要删除的学生所在的班级编号和学号,中间用空格分隔:");
int classId;
long stuId;
scanf("%d%ld", &classId, &stuId);
classPosition class = findClass(classes, classId);
if (!class) printf("\n找不到该班级!\n");
else
{
stuPosition stu = findStu(class->students, stuId);
if (!stu) printf("\n找不到该学生!\n");
else delStu(stu, &class->students);
}
break;
}
case 11:
{ //删除某个班级内的所有学生
printf("请输入要删除所有学生的班级编号:");
int classId;
scanf("%d", &classId);
classPosition class = findClass(classes, classId);
if (!class) printf("\n找不到该班级!\n");
else clearStuList(&class->students);
break;
}
case 12:
{ //删除某学生的某门成绩
printf("请输入要删除的成绩所属学生的班级编号、学号和对应的科目名称(英文),中间用空格分隔:");
int classId;
long stuId;
char subName[10];
scanf("%d%ld%s", &classId, &stuId, subName);
classPosition class = findClass(classes, classId);
if (!class) printf("\n找不到该班级!\n");
else
{
stuPosition stu = findStu(class->students, stuId);
if (!stu) printf("\n找不到该学生!\n");
else
{
subPosition sub = findSub(stu->subjects, subName);
if (!sub) printf("\n找不到该成绩!\n");
else delSub(sub, &stu->subjects);
}
}
break;
}
case 13:
{ //删除某学生的所有成绩
printf("请输入要删除所有成绩的学生所在的班级编号和学号,中间用空格分隔:");
int classId;
long stuId;
scanf("%d%ld", &classId, &stuId);
classPosition class = findClass(classes, classId);
if (!class) printf("\n找不到该班级!\n");
else
{
stuPosition stu = findStu(class->students, stuId);
if (!stu) printf("\n找不到该学生!\n");
else clearSubList(&stu->subjects);
}
break;
}
case 14:
{ //修改某学生的基本信息
printf("请输入要修改的学生所在的班级编号、学号、"
"修改后的姓名、性别、年龄和电话,中间用空格分隔(none表示不修改该项):\n");
int classId;
long stuId;
char name[10], sex[10], age[10], telephone[20];
scanf("%d%ld%s%s%s%s", &classId, &stuId, name, sex, age, telephone);
classPosition class = findClass(classes, classId);
if (!class) printf("\n该班级不存在!\n");
else
{
stuPosition stu = findStu(class->students, stuId);
if (!stu) printf("\n该学生不存在!\n");
else
{
if (strcmp(name, "none") != 0) strcpy(stu->name, name);
if (strcmp(sex, "none") != 0) stu->sex = !strcmp(sex, "male") ? 1 : 0;
if (strcmp(age, "none") != 0) stu->age = (int) strtol(age, NULL, 0);
if (strcmp(telephone, "none") != 0) stu->telephone = strtol(telephone, NULL, 0);
}
}
break;
}
case 15:
{ //修改某学生的某门成绩
printf("请输入要修改的成绩所属学生的班级编号、学号,"
"成绩对应的科目名称和修改后的成绩,中间用空格分隔(none表示不修改该项):\n");
int classId;
long stuId;
char subName[10], score[10];
scanf("%d%ld%s%s", &classId, &stuId, subName, score);
classPosition class = findClass(classes, classId);
if (!class) printf("\n该班级不存在!\n");
else
{
stuPosition stu = findStu(class->students, stuId);
if (!stu) printf("\n该学生不存在!\n");
else
{
subPosition sub = findSub(stu->subjects, subName);
if (!sub) printf("\n该成绩不存在!\n");
else
{
if (strcmp(score, "none") != 0) sub->score = (int) strtol(score, NULL, 0);
}
}
}
break;
}
case 16: // 退出学生成绩管理系统
flag = 0;
break;
default:
printf("操作代号不存在!\n");
break;
}
}
destroyClassList(&classes); // 释放内存
return 0;
}
// 科目链表操作集定义
subList createSubList() {
// 创建科目链表
subList l;
if ((l.head = l.tail = (subPosition) malloc(sizeof(struct subNode))) == NULL)
{
printf("内存分配失败!\n");
exit(1);
}
l.head->next = l.head->previous = NULL;
return l;
}
int subListEmpty(subList l) {
// 判断科目链表l是否为空
return l.head->next == NULL;
}
void insertSub(subPosition p, subList *l, char *name, int score) {
// 在科目链表l中的位置p后插入一个科目节点
subPosition temp;
if ((temp = (subPosition) malloc(sizeof(struct subNode))) == NULL)
{
printf("内存分配失败!\n");
exit(1);
}
strcpy(temp->name, name);
temp->score = score;
if (p == l->tail)
{
temp->next = p->next;
temp->previous = p;
p->next = temp;
l->tail = temp;
}
else
{
temp->next = p->next;
temp->previous = p;
temp->next->previous = temp;
p->next = temp;
}
}
void delSub(subPosition p, subList *l) {
// 删除科目链表l中位于位置p的课程节点
if (p == l->tail)
{
p->previous->next = p->next;
l->tail = p->previous;
}
else
{
p->next->previous = p->previous;
p->previous->next = p->next;
}
free(p);
}
subPosition findSub(subList l, char *name) {
//在科目链表l中查找名为name的课程
subPosition p;
for (p = l.head->next; p && strcmp(p->name, name) != 0; p = p->next);
return p;
}
void clearSubList(subList *l) {
//清空科目链表l
subPosition p, temp;
for (p = l->head->next; p; p = temp)
{
temp = p->next;
free(p);
}
l->tail = l->head;
l->head->next = NULL;
}
void printSubList(subList l) {
// 打印科目链表l
printf("\n");
subPosition p;
for (p = l.head->next; p; p = p->next)
{
printf("科目名称:%s", p->name);
printf(" 成绩:%d\n", p->score);
}
}
void destroySubList(subList *l) {
// 摧毁科目链表l
subPosition p, temp;
for (p = l->head; p; p = temp)
{
temp = p->next;
free(p);
}
l->head = l->tail = NULL;
}
// 学生链表操作集定义
stuList createStuList() {
// 创建学生链表
stuList l;
if ((l.head = l.tail = (stuPosition) malloc(sizeof(struct stuNode))) == NULL)
{
printf("内存分配失败!\n");
exit(1);
}
l.head->subjects = createSubList();
l.head->next = l.head->previous = NULL;
return l;
}
int stuListEmpty(stuList l) {
// 判断学生链表l是否为空
return l.head->next == NULL;
}
void insertStu(stuPosition p, stuList *l, long id, char *name, int sex, int age, long telephone) {
// 在学生链表l中的位置p后插入一个学生节点
stuPosition temp;
if ((temp = (stuPosition) malloc(sizeof(struct stuNode))) == NULL)
{
printf("内存分配失败!\n");
exit(1);
}
temp->id = id;
strcpy(temp->name, name);
temp->sex = sex;
temp->age = age;
temp->telephone = telephone;
temp->subjects = createSubList();
if (p == l->tail)
{
temp->next = p->next;
temp->previous = p;
p->next = temp;
l->tail = temp;
}
else
{
temp->next = p->next;
temp->previous = p;
temp->next->previous = temp;
p->next = temp;
}
}
void delStu(stuPosition p, stuList *l) {
// 删除学生链表l中位于位置p的学生节点
if (p == l->tail)
{
p->previous->next = p->next;
l->tail = p->previous;
}
else
{
p->next->previous = p->previous;
p->previous->next = p->next;
}
destroySubList(&p->subjects);
free(p);
}
stuPosition findStu(stuList l, long id) {
// 在学生链表l中查找名为name的学生
stuPosition p;
for (p = l.head->next; p && p->id != id; p = p->next);
return p;
}
void clearStuList(stuList *l) {
// 清空学生链表l
stuPosition p, temp;
for (p = l->head->next; p; p = temp)
{
temp = p->next;
destroySubList(&p->subjects);
free(p);
}
l->tail = l->head;
l->head->next = NULL;
}
void printStuList(stuList l) {
// 打印学生链表l
printf("\n");
stuPosition p;
for (p = l.head->next; p; p = p->next)
{
printf("学号:%ld", p->id);
printf(" 姓名:%s", p->name);
printf(" 性别:%s", p->sex == 1 ? "male" : "female");
printf(" 年龄:%d", p->age);
printf(" 电话:%ld\n", p->telephone);
}
}
void destroyStuList(stuList *l) {
// 摧毁学生链表l
stuPosition p, temp;
for (p = l->head; p; p = temp)
{
temp = p->next;
destroySubList(&p->subjects);
free(p);
}
l->head = l->tail = NULL;
}
// 班级链表操作集定义
classList createClassList() {
// 创建班级链表
classList l;
if ((l.head = l.tail = (classPosition) malloc(sizeof(struct classNode))) == NULL)
{
printf("内存分配失败!\n");
exit(1);
}
l.head->students = createStuList();
l.head->next = l.head->previous = NULL;
return l;
}
int classListEmpty(classList l) {
// 判断班级链表l是否为空
return l.head->next == NULL;
}
void insertClass(classPosition p, classList *l, int classId) {
// 在班级链表l中的位置p后插入一个班级节点
classPosition temp;
if ((temp = (classPosition) malloc(sizeof(struct classNode))) == NULL)
{
printf("内存分配失败!\n");
exit(1);
}
temp->classId = classId;
temp->students = createStuList();
if (p == l->tail)
{
temp->next = p->next;
temp->previous = p;
p->next = temp;
l->tail = temp;
}
else
{
temp->next = p->next;
temp->previous = p;
temp->next->previous = temp;
p->next = temp;
}
}
void delClass(classPosition p, classList *l) {
// 删除班级链表l中位于位置p的班级节点
if (p == l->tail)
{
p->previous->next = p->next;
l->tail = p->previous;
}
else
{
p->next->previous = p->previous;
p->previous->next = p->next;
}
destroyStuList(&p->students);
free(p);
}
classPosition findClass(classList l, int classId) {
// 在班级链表l中查找班级编号为classId的班级
classPosition p;
for (p = l.head->next; p && p->classId != classId; p = p->next);
return p;
}
void clearClassList(classList *l) {
// 清空班级链表l
classPosition p, temp;
for (p = l->head->next; p; p = temp)
{
temp = p->next;
destroyStuList(&p->students);
free(p);
}
l->tail = l->head;
l->head->next = NULL;
}
void printClassList(classList l) {
// 打印班级链表l
printf("\n");
classPosition p;
for (p = l.head->next; p; p = p->next)
printf("%d班\n", p->classId);
}
void destroyClassList(classList *l) {
// 摧毁班级链表l
classPosition p, temp;
for (p = l->head; p; p = temp)
{
temp = p->next;
destroyStuList(&p->students);
free(p);
}
l->head = l->tail = NULL;
}
// 用户界面操作定义
void showMainInterface() {
// 打印学生成绩管理系统主菜单
printf("\n欢迎使用学生成绩管理系统!\n");
printf("1.查看所有班级\n");
printf("2.查看某个班级内的所有学生\n");
printf("3.查看某个学生的基本信息\n");
printf("4.查看某个学生的所有成绩\n");
printf("5.插入新班级\n");
printf("6.插入新学生\n");
printf("7.插入新成绩\n");
printf("8.删除某个班级\n");
printf("9.删除所有班级\n");
printf("10.删除某个学生\n");
printf("11.删除某个班级内的所有学生\n");
printf("12.删除某学生的某个成绩\n");
printf("13.删除某个学生的所有成绩\n");
printf("14.修改某个学生基本信息\n");
printf("15.修改某学生的某个成绩\n");
printf("16.退出学生成绩管理系统\n");
}
五、运行输出结果
六、心得与体会
通过这次作业,我对于链表有了更深一层的掌握,知道了使用双向链表并完成插入、删除、修改等操作。并且,我明白了在编程过程中始终保持思路清晰的重要性,所以笔和纸必不可少,进行模块划分十分关键。当然,在这过程中我遇到了不少困难,不断试错、不断卡bug,让我摸索出了一些调试的经验,也意识到了耐心细心的可贵。