勇闯顺序表 | 附学生信息管理系统

目录

1.前言:

2.顺序表是啥?

2.1定义:

2.2存储方式:

3.顺序表的具体函数功能:

3.1定义一个顺序表(以int型为例)

3.2初始化顺序表:

3.3扩容:

​编辑3.4插入:

3.4.1:头插:

3.4.2尾插:

3.4.3任意位置插入:

 3.5删除:

3.5.1头删:

3.5.2尾删:

 3.5.3任意位置删除:

 3.6查找:

3.7销毁:

4.学生信息系统的设计

4.1录入学生记录:

4.2 添加学生信息记录:

4.3  按学号删除记录 :

4.4 按学号修改记录:

4.5按姓名查找记录 :

4.6显示所有记录 (即打印顺序表):

4.7 按照成绩排序 :

4.8 清理控制面板:

5.总代码:


 

1.前言:

       Hello everybody! 😄小邓儿,好久没更新了。今天重出江湖,带大家来唠唠顺序表。

2.顺序表是啥?

2.1定义:

顺序表:是线性表的一种物理存储结构,即顺序存储结构。指的是用一段地址连续的存储单元依此存储线性表的数据元素。

2.2存储方式:

顺序存储结构:在内存中找一块地儿,通过占位的形式,把一定内存空间给占了。然后,把相同数据类型的数据元素依次存放在这块空地上。

那到这里儿,咱们想一下,在C语言中,一个一维数组不就可以来实现相似的功能?所以,一维数组也是一种顺序存储结构,也就是顺序表。

3.顺序表的具体函数功能:

3.1定义一个顺序表(以int型为例)

这里,为什么咱们要用SLDateType来代替int给arr定义元素类型,直接用它不香么?相信很多小伙伴儿会有这个疑问。其实,也可以直接使用int,当如果你想存储的是其它类型的数据元素,如char等,这时你要将整个代码进行修改。这时SLDateType好处就体现出来了,只要修改修改typedef int SLDateType为typedef char SLDateType即可。是不是很方便( ̄▽ ̄)"

3.2初始化顺序表:

3.3扩容:

3.4插入:

3.4.1:头插:

咱们的思路就是把顺序表中元素像蓝色箭头所指,依次向后移动一个位置。然后,把要插入的数据放入第一个位置。

代码如下,不过,只要涉及插入的,都要先检查一下空间是否足够,是否要扩容哦。

3.4.2尾插:

尾插的思路就很简单了,直接在最后插入即可。

3.4.3任意位置插入:

这个听起来是不是很难,且听小邓儿来分析。假如,我们想将x插入到3这个位置,我们的思路不就是将3及后面的元素依次移动到下一个位置,再将x放到3位置上吗?

没错,思路正确。不过,元素要从后向前遍历。咱们想一下,如是从前向后会怎么样?结果是后面的数据会被覆盖,就没法实现想要的功能了。

 3.5删除:

3.5.1头删:

咱们把第一个元素删除,可以直接依次把第二个元素向前移动,最后--ps->size。

3.5.2尾删:

 3.5.3任意位置删除:

假设我们要删除3位置上的元素,只要把第4个及以后的元素依次向前移动一个位置就好了。

 3.6查找:

3.7销毁:

在使用完顺序表后,不要忘记销毁顺序表哦。这样,可以避免一些不必要的空间浪费。

4.学生信息系统的设计

 首先,让咱们来看看具体要实现的功能有哪些(●'◡'●)

4.1录入学生记录:

此功能小邓儿用了二进制文件来录入。具体代码如下,感兴趣的小伙伴儿可以去了解一下二进制文件的相关概念。

//文件存储
int SaveList(SL* L, const char* filename)
{
	FILE* fp = fopen(filename, "wb");
	if (fp == NULL)
	{
		perror("文件打开失败");
		return -1;
	}
	size_t written = fwrite(L->data,sizeof(Stu), L->length, fp);
	fclose(fp);
	return (written == L->length) ? 0 : -1; 
}
// 写入可读文件
int SaveText(SL* list, const char* filename) {
	FILE* fp = fopen(filename, "w");
	if (!fp) return -1;

	for (int i = 0; i < list->length; i++) {
		fprintf(fp, "%s,%s,%.1f\n",
			list->data[i].id,
			list->data[i].name,
			list->data[i].score);
	}
	fclose(fp);
	return 0;
}
// 读取文件函数
int LoadList(SL* list, const char* filename)
{
	FILE* fp = fopen(filename, "rb");
	if (fp == NULL) {
		return -1;
	}
	// 获取文件大小并计算记录数
	fseek(fp, 0, SEEK_END);
	long file_size = ftell(fp);
	rewind(fp);
	int num_records = file_size / sizeof(Stu);
	if (file_size % sizeof(Stu) != 0) {
		fclose(fp);
		return -1; // 文件内容不完整
	}
	// 检查容量是否足够
	if (num_records > MaxSize) {
		fclose(fp);
		return -1; // 超出存储容量
	}
	// 读取数据
	size_t read = fread(list->data, sizeof(Stu), num_records, fp);
	fclose(fp);
	if (read != num_records) {
		return -1; // 读取不完整
	}
	list->length = num_records;
	return 0;
}
//异常处理
bool Input_compliant(char* stunum, char* stuname, float stuscore)
{// 进一步判断输入的学号、姓名和成绩是否合理
	// 遍历每个字符进行校验
	for (; *stunum != '\0'; stunum++) {//学号只能由数字构成
		if (!isdigit((unsigned char)*stunum)) {
			return false;  // 发现非数字字符立即返回失败
		}
	}
	for (; *stuname != '\0'; stuname++) {
		unsigned char c = (unsigned char)*stuname;
		if (!isalpha(c)) { // 允许空格和连字符
			return false;
		}
	}
	if (stuscore < 0 || stuscore>100) { //成绩应在合理区间[0,100]
		return false;
	}
	return true;
}
void Input_stuinfo(SL* L)
{//读取用户输入并写入线性表
	int info_num = L->length;
	char inputinfo[100];
	printf("请按照以下格式录入学生信息(8052 zhangsan 89),以'#'结束!\n");
	while (1)
	{
		char stunum[10] = { 0 };
		char stuname[20] = { 0 };
		float stuscore;
		fgets(inputinfo, sizeof(inputinfo), stdin);
		inputinfo[strcspn(inputinfo, "\n")] = '\0';  // 去除末尾换行符

		// 检查是否输入终止符 `#`
		if (strcmp(inputinfo, "#") == 0) {
			printf("停止录入,正在返回主界面...\n");
			break;
		}
		// 解析输入的Num_Data_Items个字段
		if (sscanf(inputinfo, "%s %s %f", stunum, stuname, &stuscore) != Num_Data_Items)
		{
			printf("信息未通过形式审查,建议检查并删除多余空格,然后重新录入...\n");
			continue;
		}
		else
		{
			if (!Input_compliant(stunum, stuname, stuscore))
			{
				printf("错误!学号由数字构成,姓名由字母构成,成绩要在[1,100]之间,请重新录入...\n");
				continue;
			}
			printf("输入的学号、姓名、成绩符合规定,正在录入顺序表...\n");
			strcpy(L->data[info_num].id, stunum);
			strcpy(L->data[info_num].name, stuname);
			L->data[info_num].score = stuscore;
			info_num++;
			printf("录入成功!请继续录入下一条学生信息,以'#'结束!...\n");
		}
	}
	L->length = info_num;
}

4.2 添加学生信息记录:

这里,咱们用的就是刚刚任意位置插入的方法。

4.3  按学号删除记录 :

同理,这里,咱们用的就是刚刚任意位置删除的方法。

4.4 按学号修改记录:

这里的Change函数,小邓儿用的是strcmp函数 。

4.5按姓名查找记录 :

这里的Search函数,其实就是在刚刚的 SLFind函数上做了一个小小的变动,大体思路都是遍历元素,看看是否相同。

4.6显示所有记录 (即打印顺序表):

4.7 按照成绩排序 :

这里,小邓儿用的是冒泡排序法,如果大家还有更好的方法,欢迎评论留言哦。

4.8 清理控制面板:

咱们直接使用system("cls")这条语句就可以实现啦。

5.总代码:

#define  _CRT_SECURE_NO_WARNINGS
#define MaxSize 200  // 顺序表最大元素数量
#define Num_Data_Items 3  // 每个数据元素的数量
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include <ctype.h>
#include<assert.h>
#include<stdbool.h>
typedef struct Student
{
	char id[10];
	char name[20];
	float score;
}Stu;
typedef struct SeqList
{
	Stu* data;
	int length;
	int capacity;
}SL;
//初始化顺序表
void InitList(SL** L)
{
    *L = (SL*)malloc(sizeof(SL));
    (*L)->data = (Stu*)malloc(4 * sizeof(Stu));  
    (*L)->length = 0;
    (*L)->capacity = 4;
}
//扩容:当空间不足时自动扩容(每次增加5个位置)
void CheckCapacity(SL* L)
{
	if (L->length >=L->capacity)
	{
		int newCapacity = (L->capacity == 0) ? 4 : L->capacity + 5;
		Stu* tmp = realloc(L->data,newCapacity * sizeof(Stu));
		if (!tmp)
		{
			printf("扩容失败!\n");
			exit(1);
		}
		L->data= tmp;
		L->capacity = newCapacity;
	}
}
//插入记录
void Insert(SL* L, int pos, Stu s) {
	assert(L);
	assert(pos >= 0 && pos <= L->length);
	CheckCapacity(L);
	// 从后往前移动元素(包含pos位置)
	for (int i = L->length; i > pos; i--) {
		L->data[i] = L->data[i - 1];
	}
	L->data[pos] = s;
	L->length++;
}
//查找功能:按姓名查找,返回第一个匹配的位置
int Search(SL*L, char* name)
{
	for (int i = 0; i < L->length; i++)
	{
		if (strcmp(L->data[i].name,name)==0)
		{
			return i;
		}
	} return -1;
}
//删除:按学号查找并删除
void Delete(SL* L, char* id)
{
	for (int i = 0; i < L->length; i++)
	{
		if (strcmp(L->data[i].id,id) == 0)
		{
			for (int j = i; j < L->length-1; j++)
			{
				L->data[j] = L->data[j + 1];
			}
		}
	}
	L->length--;
}
//修改学生信息
void Change(SL*L,char* id)
{
	int found = 0;
	for (int i = 0; i < L->length; i++) {
		if (strcmp(L->data[i].id, id) == 0) {
			printf("找到学生,请输入新信息(学号 姓名 成绩):\n");
			scanf("%s%s%f", L->data[i].id, L->data[i].name, &L->data[i].score);
			found = 1;
			break; // 修改后退出循环
		}
	}
	if (!found) {
		printf("未找到学号为 %s 的学生!\n", id);
	}
}

//输出
void DispList(SL*L)
{
	for (int i = 0; i < L->length; i++)
	{
		printf("学号:%s   姓名:%s   成绩:%f\n",L->data[i].id, L->data[i].name,L->data[i].score);
	}
}
//销毁
void Destroy(SL* L)
{
	assert(L);
	if (L->data)
		free(L->data);
	L->data = NULL;
	L->length = L->capacity = 0;
}
//创新功能:按学生成绩排序(从高到低)
void score_sort(SL* L)
{
	for (int i = 0; i < L->length-1; i++)
	{
		for (int j = 0; j < L->length - i - 1; j++)
		{
			if (L->data[j].score<L->data[j + 1].score)
			{
				Stu temp = L->data[j];
				L->data[j] = L->data[j + 1];
				L->data[j + 1] = temp;
			}
		}
	}
}
//文件存储
int SaveList(SL* L, const char* filename)
{
	FILE* fp = fopen(filename, "wb");
	if (fp == NULL)
	{
		perror("文件打开失败");
		return -1;
	}
	size_t written = fwrite(L->data,sizeof(Stu), L->length, fp);
	fclose(fp);
	return (written == L->length) ? 0 : -1; 
}
// 写入可读文件
int SaveText(SL* list, const char* filename) {
	FILE* fp = fopen(filename, "w");
	if (!fp) return -1;

	for (int i = 0; i < list->length; i++) {
		fprintf(fp, "%s,%s,%.1f\n",
			list->data[i].id,
			list->data[i].name,
			list->data[i].score);
	}
	fclose(fp);
	return 0;
}
// 读取文件函数
int LoadList(SL* list, const char* filename)
{
	FILE* fp = fopen(filename, "rb");
	if (fp == NULL) {
		return -1;
	}
	// 获取文件大小并计算记录数
	fseek(fp, 0, SEEK_END);
	long file_size = ftell(fp);
	rewind(fp);
	int num_records = file_size / sizeof(Stu);
	if (file_size % sizeof(Stu) != 0) {
		fclose(fp);
		return -1; // 文件内容不完整
	}
	// 检查容量是否足够
	if (num_records > MaxSize) {
		fclose(fp);
		return -1; // 超出存储容量
	}
	// 读取数据
	size_t read = fread(list->data, sizeof(Stu), num_records, fp);
	fclose(fp);
	if (read != num_records) {
		return -1; // 读取不完整
	}
	list->length = num_records;
	return 0;
}
//异常处理
bool Input_compliant(char* stunum, char* stuname, float stuscore)
{// 进一步判断输入的学号、姓名和成绩是否合理
	// 遍历每个字符进行校验
	for (; *stunum != '\0'; stunum++) {//学号只能由数字构成
		if (!isdigit((unsigned char)*stunum)) {
			return false;  // 发现非数字字符立即返回失败
		}
	}
	for (; *stuname != '\0'; stuname++) {
		unsigned char c = (unsigned char)*stuname;
		if (!isalpha(c)) { // 允许空格和连字符
			return false;
		}
	}
	if (stuscore < 0 || stuscore>100) { //成绩应在合理区间[0,100]
		return false;
	}
	return true;
}
void Input_stuinfo(SL* L)
{//读取用户输入并写入线性表
	int info_num = L->length;
	char inputinfo[100];
	printf("请按照以下格式录入学生信息(8052 zhangsan 89),以'#'结束!\n");
	while (1)
	{
		char stunum[10] = { 0 };
		char stuname[20] = { 0 };
		float stuscore;
		fgets(inputinfo, sizeof(inputinfo), stdin);
		inputinfo[strcspn(inputinfo, "\n")] = '\0';  // 去除末尾换行符

		// 检查是否输入终止符 `#`
		if (strcmp(inputinfo, "#") == 0) {
			printf("停止录入,正在返回主界面...\n");
			break;
		}
		// 解析输入的Num_Data_Items个字段
		if (sscanf(inputinfo, "%s %s %f", stunum, stuname, &stuscore) != Num_Data_Items)
		{
			printf("信息未通过形式审查,建议检查并删除多余空格,然后重新录入...\n");
			continue;
		}
		else
		{
			if (!Input_compliant(stunum, stuname, stuscore))
			{
				printf("错误!学号由数字构成,姓名由字母构成,成绩要在[1,100]之间,请重新录入...\n");
				continue;
			}
			printf("输入的学号、姓名、成绩符合规定,正在录入顺序表...\n");
			strcpy(L->data[info_num].id, stunum);
			strcpy(L->data[info_num].name, stuname);
			L->data[info_num].score = stuscore;
			info_num++;
			printf("录入成功!请继续录入下一条学生信息,以'#'结束!...\n");
		}
	}
	L->length = info_num;
}
void show_screen()
{
	printf("***************       功能选择      *****************\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");
}
int main()
{
	int choose; int i;
	char filename_dat[] = "student_info.dat";
	char filename_text[] = "student_info.text";
	SL* stulist;  //定义一个指向线性表的指针
	InitList(&stulist); //初始化一个学生元素的线性表
	int info_num = 0;
	if (LoadList(stulist, filename_dat) == 0) {
		printf("加载成功!共%d条记录\n", stulist->length);
	}
	while (1)
	{
		show_screen();
		printf("请选择:\n");
		scanf("%d", &choose);
		int c;
		Stu s = { 0,0,0 };
		while ((c = getchar()) != '\n' && c != EOF);//清空scanf缓冲区
		switch (choose)
		{
		case 1://录入学生信息
			Input_stuinfo(stulist);
			SaveList(stulist, filename_dat);
			SaveText(stulist, filename_text);  // 可读文本备份
			break;
		case 2:	
			 //在指定序号i处增加学生信息,并将原表受影响的学生后移
			printf("要修改的地址:\n");
			scanf("%d", &i);
			while ((c = getchar()) != '\n' && c != EOF); // 清理缓冲区
			printf("请输入学号、姓名、成绩:\n");
			scanf("%s%s%f", s.id, s.name, &s.score);
			Insert(stulist, i, s);
			printf("添加成功!请继续。\n");
			break;
		case 3://按学号删除学生信息,并将原表中后面的学生前移
			printf("请输入要删除学生的学号:\n");
			scanf("%s", s.id);
			Delete(stulist, s.id);
			printf("删除成功!请继续。\n");
			break;
		case 4://按学号修改学生信息
			printf("要修改的学生学号:\n");
			scanf("%s",s.id);
			while ((c = getchar()) != '\n' && c != EOF);
			Change(stulist, s.id);
			printf("修改成功!请继续。\n");
			break;
		case 5://按照姓名查询学生信息
			printf("要查找的学生姓名:\n");
			scanf("%s", s.name);
			int index = Search(stulist,s.name);
			if (index != -1)
			{
				printf("找到学生,信息如下:\n");
				printf("学号:%s   姓名:%s   成绩:%.1f\n",
					stulist->data[index].id,
					stulist->data[index].name,
					stulist->data[index].score);
			}
			else {
				printf("未找到姓名为 %s 的学生!\n", s.name);
			}
			break;
		case 6://显示所有记录
			DispList(stulist);
			break;
		case 7://按照成绩排序 
			score_sort(stulist);
			printf("已按成绩从高到低排序,并保存到文件。\n");
			SaveList(stulist, filename_dat);
			SaveText(stulist, filename_text);
			break;
		case 8://清屏
			system("cls");
			break;
		case 9://退出管理系统
			printf("退出系统,拜拜!");
			return;
		default:
			printf("功能尚未开放!\n");
			break;
		}
		if (choose == 8&&choose==9)//程序终止
		{
			Destroy(stulist);
			break;
		}
	}
	return 0;
}

今天,小邓儿带大家用顺序表,实现了学生信息系统的功能。希望以后咱们一起努力,成为厉害的程序猴(●'◡'●)

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值