这是作者大一时候的课程作业,多年后发现还保留着,就放上来分享下。
包含基本的数据结构设计;数据的录入、增删查改、排序、统计和全输出;和最后的退出自动保存数据等基本操作。
这里做的是学生信息管理系统,读者可以自行改成图书管理系统等其他信息管理系统,难度不大。可以直接复制代码到IDE里跑。
话不多说,直接上头文件:
//head.h
#include<stdio.h>
#include<malloc.h>//分配空间头文件
#include<string.h>//字符串头文件
#include<string>//C++字符串头文件
#include<queue>//队列头文件,优先队列会用到
#include<stdlib.h>//一些界面操作函数会用到
#include <unistd.h>//清空文件的函数ftruncate
#define MaxSize 100//这个是控制最大节点量的,可调大
using namespace std;//C++的默认命名空间
//定义信息节点类型
typedef struct SNode
{
long long student_number;//学号
char name[20];//姓名
char gender[10];//性别
char academy[20];//学院
char major[20];//专业班级
int English;//英语成绩
int computer;//计算机成绩
int math;//数学成绩
int total_points;//总分
int length;//这个是统计节点数目的数据
bool operator<(const SNode& lhs)const{return lhs.total_points>total_points;}//优先队列运算符重载,不懂可百度,可重载<,>,=
struct SNode *next;//节点指针
}ADDR;
//以下是菜单功能函数
void welcome();//欢迎界面
void luru(ADDR *&h);//录入
void deletedata(ADDR *&h);//删除
void add_data(ADDR *&h);//增添
void find_data(ADDR *&h);//查找
void sort_data(ADDR *&h);//排序
void statistics(ADDR *&h);//统计
void modification(ADDR *&h);//修改
void save(ADDR *&h);//写入磁盘文件
//以下是数据结构函数
void InitList(ADDR *&L);//给链表分配空间
void DestroyList(ADDR *&L);//删除链表
void DispList(ADDR *L);//输出全链表
bool GetElem(ADDR *L,int i);//输出节点数据
int LocateElem(ADDR *L,long long e);//按学号查找
bool ListInsert(ADDR *&L,int &i);//插入节点
bool ListInsert2(ADDR *&L,int &i,FILE *&fp);//读取文件
bool ListDelete(ADDR *&L,int i);//删除节点
int LocateElem2(ADDR *L,char s[]);//按姓名查找
bool modification_data(ADDR *&h,int i);//修改数据
void read_in(ADDR *L,FILE *&fp);//写入文件
头文件包含了整个系统的结构和目录,看多几遍,下面的就好理解了。
主函数(main.cpp),系统的入口,读者可以从这里开始理清整个系统结构:
//main.cpp
#include "head.h"
void Menu_select(ADDR *&h);//功能选择函数
int main()
{
int n;
ADDR *top;//保存数据节点的单链表
InitList(top);//初始化单链表
top->length=0;//目前没有节点
welcome();//用户交互页面
Menu_select(top);//选择执行的操作
DestroyList(top);//退出系统前删除数据链表
printf("\tGoodbye!\n");
return 0;
}
void Menu_select(ADDR *&h)
{
int cn=0;//保存操作序号
while(scanf("%d",&cn)!=EOF)
{
if(cn<1|| cn>9 )//这里只有9个功能
{
printf("\n\t输入错误,重选1-9:");
continue;
}
switch(cn)
{
case 1://录入数据
{
luru(h);
break;
}
case 2://删除数据
{
deletedata(h);
break;
}
case 3://添加数据
{
add_data(h);
break;
}
case 4://查询数据
{
find_data(h);
break;
}
case 5://排序数据
{
sort_data(h);
break;
}
case 6://统计数据
{
statistics(h);
break;
}
case 7://修改数据
{
modification(h);
break;
}
case 8://按录入顺序全输出数据
{
printf("\t学生的基本信息如下\n\t(格式为:学号 姓名 性别 学院 专业班级 外语成绩 计算机成绩 数学成绩 三科总分):\n");
DispList(h);
printf("\n");
printf("\n\t按回车跳回主菜单!\n");
getchar();
getchar();
system("cls");
break;
}
case 9://保存并退出
{
save(h);
return ;
}
}
welcome();//交互页面
}
}
欢迎页面(或者说是交互页面):
//交互页面.cpp。
#include "head.h"
void welcome(){
/*
printf("\t\t\t\t\t* 欢迎来到学生信息管理系统 *\n");
printf("\t**********************************************************************************\n");
printf("\t* 数据保存在D://学生信息数据.txt中,若文件不存在请先进行创建,请知悉! *\n");
printf("\t* ▂▃▅▆█谢谢使用!█▆▅▃▂ *\n");
printf("\t**********************************************************************************\n");
printf("\n");
printf("\n按回车继续。\n");
getchar();
system("cls");
printf("\n");
*/
printf("\t\t\t* 欢迎来到学生信息管理系统 *\n");
printf("\t************************************************************\n");
printf("\t* [1] 录入学生信息 [2] 删除学生信息 *\n");
printf("\t* [3] 增加学生信息 [4] 查找学生信息 *\n");
printf("\t* [5] 排序学生信息 [6] 统计学生信息 *\n");
printf("\t* [7] 修改学生信息 [8] 输出学生信息 *\n");
printf("\t* [9] 保存学生信息并退出 *\n");
printf("\t************************************************************\n");
printf("\t左边数字对应功能选择,请选1-9:");
}
下面是各个功能函数以及该功能涉及的数据结构操作函数(按操作序号排序):
1.录入数据:
//录入数据.cpp。值得注意的是,这个系统用的是文件本保存数据,相当于用文件本代替数据库,进而简化系统的理解难度。已经学习数据库的读者可以自行修改代码代换成数据库。
//这里的录入数据,指的是从文件本中逐行读取数据,然后添加到数据链表上以便进一步操作,退出系统同理,将数据链表写入文件本中,直接覆盖原数据,从而达到修改保存的目的。
#include "head.h"
void luru(ADDR *&h)//函数这里*&h中有讲究,有&则代表将改变数据链表的内容,无&则只是读取,读者自行体会
{
int pd;
FILE *fp;//定义文件本指针
if((fp=fopen("D://学生信息数据.txt","r"))==NULL)//这里判断文件是否存在
{
printf("文件不存在。\n");
printf("跳回主菜单!\n");
getchar();
system("cls");
return ;
}
while(fscanf(fp,"%d",&pd)!=EOF&&pd==1)//逐行读取文件本数据
{
ListInsert2(h,h->length,fp);//将数据写入数据链表
}
fclose(fp);//关闭流
printf("\n\t录入成功,按回车键继续\n");
getchar();
getchar();
system("cls");
}
bool ListInsert2(ADDR *&L,int &i,FILE *&fp)//这就是简单的节点插入链表的操作
{
i++;
int j=0;
ADDR *p=L,*s;
while(j<i-1&&p!=NULL)//找到链表插入点
{
j++;
p=p->next;
}
if(p==NULL)
return false;
else
{
s=(ADDR *)malloc(sizeof(ADDR));
fscanf(fp,"%lld %s %s %s %s %d %d %d",&s->student_number,&s->name,&s->gender,&s->academy,&s->major,&s->English,&s->computer,&s->math);//这里比较讲究,根据数据对齐转换为节点的各个属性成员
s->total_points=s->English+s->computer+s->math;//算个总分
s->next=p->next;//插入
p->next=s;
return true;
}
}
2.删除数据:
//删除数据.cpp。删除比较简单,根据学号找到对应的数据,执行单链表的删除操作即可。
#include "head.h"
void deletedata(ADDR *&h)
{
int z;
do
{
printf("\t请输入学生学号:");
long long ID;
scanf("%lld",&ID);
int i=LocateElem(h,ID);//查找该学号的节点位置
while(i==0)
{
printf("\t该学生学号不存在!\n");
printf("\t请输入正确的学生学号:");
scanf("%lld",&ID);
i=LocateElem(h,ID);
}
printf("\t该学生数据如下如下:\n\t");
GetElem(h,i);
printf("\t是否执行删除操作?(确定输入1,取消输入0)");
int j;
scanf("%d",&j);
if(j==1)
{
bool pd=ListDelete(h,i);//单链表的删除节点操作
if(pd) printf("\t删除成功。\n");
else printf("\t删除失败。\n");
}
printf("\t是否继续执行删除操作(输入1为继续删除,输入0结束删除系统):");
}while(scanf("%d",&z)!=EOF&&z==1);
system("cls");
}
bool ListDelete(ADDR *&L,int i)//单链表的删除节点操作
{
int j=0;
ADDR *p=L,*q;
while (j<i-1&&p!=NULL)//找到要删除的节点
{
j++;
p=p->next;
}
if(p==NULL)
return false;
else
{
q=p->next;
if(q==NULL)
return false;
p->next=q->next;
free(q);
L->length--;
return true;
}
}
3.增添数据:
//增添数据.cpp。单链表的增添节点操作
#include "head.h"
void add_data(ADDR *&h)
{
int z;
do
{
bool pd=ListInsert(h,h->length);
if(pd) printf("\t增添成功!\n");
else printf("\t增添失败!\n");
printf("\t是否继续执行增添操作(输入1为继续增添,输入0返回主菜单):");
}while(scanf("%d",&z)!=EOF&&z==1);
system("cls");
}
bool ListInsert(ADDR *&L,int &i)
{
i++;
int j=0;
ADDR *p=L,*s;
while(j<i-1&&p!=NULL)
{
j++;
p=p->next;
}
if(p==NULL)
return false;
else
{
s=(ADDR *)malloc(sizeof(ADDR));
printf("\t请输入录入学生的学号:");
scanf("%lld",&s->student_number);
printf("\t请输入录入学生的姓名:");
scanf("%s",&s->name);
printf("\t请输入录入学生的性别:");
scanf("%s",&s->gender);
printf("\t请输入录入学生的学院:");
scanf("%s",&s->academy);
printf("\t请输入录入学生的专业班级:");
scanf("%s",&s->major);
printf("\t请输入录入学生的外语成绩:");
scanf("%d",&s->English);
printf("\t请输入录入学生的计算机成绩:");
scanf("%d",&s->computer);
printf("\t请输入录入学生的数学成绩:");
scanf("%d",&s->math);
s->total_points=s->English+s->computer+s->math;
s->next=p->next;
p->next=s;
return true;
}
}
4.查询数据:
//查询数据.cpp。这里分了学号查询和姓名查询两种方式,分别是整数查询和字符串查询,比较简单,不多解释。
#include "head.h"
void find_data(ADDR *&h)
{
int z;
do
{
printf("\t【1】学号查询 【2】姓名查询\n");
printf("\t请选择你要进行的查询操作(1~2):");
int i;
scanf("%d",&i);
while(i!=1&&i!=2)
{
printf("\t输入有误,请重新输入。");
printf("\t请选择你要进行的查询操作(1~2):");
scanf("%d",&i);
}
if(i==1)
{
long long ID;
printf("\t请输入要查询学生的学号:");
scanf("%lld",&ID);
int i;
i=LocateElem(h,ID);
if(i==0) printf("\t不存在该学生学号,查找失败\n");
else
{
printf("\t该学生的基本信息如下\n\t(格式为:学号 姓名 性别 学院 专业班级 外语成绩 计算机成绩 数学成绩 三科总分):\n");
GetElem(h,i);
}
}
else
{
printf("\t请输入要查询学生的姓名:");
char name[20];
scanf("%s",&name);
int j;
j=LocateElem2(h,name);
if(j==0) printf("\t不存在该学生姓名,查找失败\n");
else
{
printf("\t该学生的基本信息如下(格式为:学号 姓名 性别 学院 专业班级 外语成绩 计算机成绩 数学成绩 三科总分):\n");
GetElem(h,j);
}
}
printf("\t是否继续执行查询操作(输入1为继续查询,输入0结束查询系统):");
}while(scanf("%d",&z)!=EOF&&z==1);
system("cls");
}
bool GetElem(ADDR *L,int i)
{
int j=0;
ADDR *p=L;
while(j<i&&p!=NULL)
{
j++;
p=p->next;
}
if(p==NULL)
return false;
else
{
printf("\t%lld %s %s %s %s %d %d %d %d\n",p->student_number,p->name,p->gender,p->academy,p->major,p->English,p->computer,p->math,p->total_points);
return true;
}
}
int LocateElem(ADDR *L,long long e)
{
int i=1;
ADDR *p=L->next;
while(p!=NULL&&p->student_number!=e)
{
p=p->next;
i++;
}
if(p==NULL)
return (0);
else
return (i);
}
int LocateElem2(ADDR *L,char s[])
{
int i=1;
ADDR *p=L->next;
while(p!=NULL&&strcmp(p->name,s)!=0)
{
p=p->next;
i++;
}
if(p==NULL)
return (0);
else
return (i);
}
5.排序数据:
//排序数据.cpp。排序这的代码比较简洁,但用到了C++的STL(标准模板库)。用了优先队列和运算符重载,不懂可以百度。
#include "head.h"
void sort_data(ADDR *&h)
{
printf("\t以下是学生总分排名:\n");
ADDR *s=h;
SNode g;
priority_queue<SNode>q;//定义优先队列
for(int i=0;i<h->length;i++)//将节点逐个压入优先队列中
{
s=s->next;
g.student_number=s->student_number;
strcpy(g.name,s->name);
strcpy(g.gender,s->gender);
strcpy(g.academy,s->academy);
strcpy(g.major,s->major);
g.English=s->English;
g.computer=s->computer;
g.math=s->math;
g.total_points=s->total_points;
q.push(g);
}
SNode p;
while(!q.empty())//再逐个输出即可
{
p=q.top();
printf("\t%lld %s %s %s %s %d %d %d %d\n",p.student_number,p.name,p.gender,p.academy,p.major,p.English,p.computer,p.math,p.total_points);
q.pop();
}
printf("\n\t按回车跳回主菜单!\n");
getchar();
getchar();
system("cls");
}
6.统计数据:
//统计数据.cpp。这里分为学院人数统计和班级人数统计两种,代码较简单,读者可自行理解。
#include "head.h"
void statistics(ADDR *&h)
{
char academy[100][20]={0};
int vis[100]={0};
int sum=0;
ADDR *s=h;
while(s->next!=NULL)
{
s=s->next;
int j;
for(j=0;j<sum;j++)
{
if(strcmp(academy[j],s->academy)==0)
break;
}
if(j==sum)
{
strcpy(academy[sum],s->academy);
vis[sum]=1;
sum++;
}
else
{
vis[j]++;
}
}
printf("\n\t学院人数统计:\n");
for(int i=0;i<sum;i++)
{
printf("\t%s\t",academy[i]);
printf("\t %d 人。\n",vis[i]);
}
char major[100][20]={0};
int vis2[100]={0};
int sum2=0;
ADDR *s2=h;
while(s2->next!=NULL)
{
s2=s2->next;
int k;
for(k=0;k<sum2;k++)
{
if(strcmp(major[k],s2->major)==0)
break;
}
if(k==sum2)
{
strcpy(major[k],s2->major);
vis2[sum2]=1;
sum2++;
}
else
{
vis2[k]++;
}
}
printf("\n\t班级人数统计:\n");
for(int i=0;i<sum2;i++)
{
printf("\t%s\t",major[i]);
printf("\t %d 人。\n",vis2[i]);
}
printf("\n\t按回车跳回主菜单!\n");
getchar();
getchar();
system("cls");
}
7.修改数据:
//修改数据.cpp。前一步是查找该节点,后一步为修改相关属性成员,读者可自行理解。
#include "head.h"
void modification(ADDR *&h)
{
int z;
do
{
printf("\t请输入修改学生的学号:");
long long ID;
scanf("%lld",&ID);
int i;
i=LocateElem(h,ID);
while(i==0)
{
printf("\t不存在该学生学号!\n");
printf("\t请输入正确的学生学号:");
scanf("%lld",&ID);
i=LocateElem(h,ID);
}
printf("\t该学生基本信息如下:\n");
GetElem(h,i);
printf("\t是否执行修改操作?(确定输入1,取消输入0)");
int j;
scanf("%d",&j);
if(j==1)
{
bool pd=modification_data(h,i);
if(pd) printf("\t修改成功。\n");
else printf("\t修改失败。\n");
}
printf("\t是否继续执行修改操作(输入1为继续修改,输入0结束修改系统):");
}while(scanf("%d",&z)!=EOF&&z==1);
system("cls");
}
bool modification_data(ADDR *&h,int i)
{
ADDR *s=h;
for(int j=0;j<i;j++)
{
s=s->next;
}
printf("\t(1)姓名 (2)性别 (3)学院 (4)专业班级\n\t(5)外语 (6)计算机 (7)数学\n");
printf("\t请选择你要修改该学生的哪一项信息:");
int d;
scanf("%d",&d);
while(d<1||d>7)
{
printf("\t输入有误,请重新输入(1~7):");
scanf("%d",&d);
}
switch(d)
{
case 1:
{
printf("\t请输入修改的姓名:");
scanf("%s",&s->name);
break;
}
case 2:
{
printf("\t请输入修改的性别:");
scanf("%s",&s->gender);
break;
}
case 3:
{
printf("\t请输入修改的学院:");
scanf("%s",&s->academy);
break;
}
case 4:
{
printf("\t请输入修改的专业班级:");
scanf("%s",&s->major);
break;
}
case 5:
{
printf("\t请输入修改的英语成绩:");
scanf("%d",&s->English);
break;
}
case 6:
{
printf("\t请输入修改的计算机成绩:");
scanf("%d",&s->computer);
break;
}
case 7:
{
printf("\t请输入修改的数学成绩:");
scanf("%d",&s->math);
break;
}
}
s->total_points=s->English+s->computer+s->math;
return true;
}
8.输出数据(该功能在主函数.cpp和公用函数.cpp中):
//公用函数.cpp。
#include "head.h"
void InitList(ADDR *&L)
{
L=(ADDR *)malloc(sizeof(ADDR));
L->next=NULL;
}
void DispList(ADDR *L)
{
ADDR *s=L->next;
while(s!=NULL)
{
printf("\t%lld %s %s %s %s %d %d %d %d\n",s->student_number,s->name,s->gender,s->academy,s->major,s->English,s->computer,s->math,s->total_points);
s=s->next;
}
printf("\n");
}
void DestroyList(ADDR *&L)//删除数据链表,释放空间
{
ADDR *p=L,*q=p->next;
while (q!=NULL)
{
free(p);
p=q;
q=p->next;
}
free(p);
}
9.退出并保存:
//保存数据.cpp。这里是第九项操作退出系统的前缀步骤,把修改完的数据链重写覆盖到文件本里,实现数据的更新。
#include "head.h"
void save(ADDR *&h)
{
printf("\t保存中...\n");
FILE *fp;
if((fp=fopen("E://学生信息数据 .txt","w"))==NULL)
{
printf("\t读取磁盘文件失败。\n");
return ;
}
read_in(h,fp);
fclose(fp);
printf("\t保存成功!\n");
}
void read_in(ADDR *L,FILE *&fp)
{
int fd = fileno(fp);
ftruncate(fd,0);
rewind(fp);
ADDR *s=L->next;
while(s!=NULL)
{
fprintf(fp,"1 %lld %s %s %s %s %d %d %d\n",s->student_number,s->name,s->gender,s->academy,s->major,s->English,s->computer,s->math);
s=s->next;
}
printf("\n");
}
下面附带一份测试数据(将测试数据复制粘贴到D://学生信息数据.txt中即可):
1 52413404201 北乔峰 男 信息学院 软件工程2班 90 98 94
1 52413404202 南慕容 男 信息学院 软件工程2班 93 94 99
1 52413404203 霍建华 男 信息学院 软件工程2班 92 98 93
1 52413404241 匹马呀 男 信息学院 软件工程2班 100 100 100
1 52413403468 方冰冰 女 经贸学院 经济管理3班 98 87 46
1 52413404242 周星星 女 信息学院 软件工程2班 95 94 100
1 52413404141 陈正城 男 信息学院 软件工程1班 97 97 97
1 52413404888 段正淳 男 金庸学院 挖掘机1班 97 97 64
1 52413404211 小龙女 女 信息学院 软件工程2班 97 98 98
1 52413404240 全真子 男 信息学院 软件工程2班 95 94 93
1 52413404238 剁剁手 男 边锋学院 国战专业 59 86 64
1 52413404241 当阳啊 男 信息学院 软件工程2班 99 100 100
下面是部分测试结果:
如有不清楚的,可以评论留言。