顺序表实现学生信息管理(C语言)
文章目录
-
- SeqList.h
- main.c
- SeqList
用顺序表实现学生信息管理。这里放一下有哪些文件。
下面是具体实现的代码。
SeqList.h
#pragma once防止库函数的重复引用,因为库函数会在预编译的时候在程序中展开,会增大程序的体积
#pragma once#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
// 顺序表中存储的数据类型,可以根据自己的需要修改
typedef struct student {char name[20];char sex[5];int sno;int age;
}SLDataType;
typedef struct SeqList {SLDataType* a; //指向动态开辟的数组int size; //记录存储数据个数int capacity; //空间容量大小
}SL;// 打印
void SLPrint(SL* ps);// 初始化与销毁
void SLInit(SL* ps);
void SLDestroy(SL* ps);// 尾插尾删
void SLPushBack(SL* ps, SLDataType* x);
void SLPopBack(SL* ps);// 头插头删
void SLPushFront(SL* ps, SLDataType* x);
void SLPopFront(SL* ps);// 顺序表查找
int SLFind(SL* ps, int x);
int Find(SL* ps, int sno);// 顺序表在pos位置插入x
void SLInsert(SL* ps, int pos, SLDataType* x);
// 顺序表删除pos位置的值
void SLErase(SL* ps, int pos);// 扩容函数
void SLCheckCapacity(SL* ps);
main.c
因为重点在于数据结构顺序表的使用,所以直接给定一些数据,就不进行重复繁琐的数据输入工作了。
#include "SeqList.h"void TestSeqList()
{SL sl;SLInit(&sl);SLDataType stu1 = { "张三", "男", 110701, 22 };SLDataType stu2 = { "李四", "男", 110702, 21 };SLDataType stu3 = { "王五", "女", 110703, 23 };SLDataType stu4 = { "赵六", "女", 110704, 22 };SLDataType stu5 = { "周七", "男", 110705, 23 };SLPushBack(&sl, &stu1);SLPushBack(&sl, &stu2);SLPushBack(&sl, &stu3);SLPushBack(&sl, &stu4);SLPrint(&sl);SLInsert(&sl, 2, &stu5);SLPrint(&sl);// 查找可以有多种方式,按照不同的信息查找,这里只是以学号为例int n = SLFind(&sl, 110703);printf("%d\\n", n);SLErase(&sl, 2);SLPrint(&sl);SLDestroy(&sl);
}
int main()
{TestSeqList();return 0;
}
SeqList
打印函数的实现,如果顺序表中的数据类型发生了改变,其他功能函数基本上不需要有什么变化, 打印函数对应修改一下就行了,毕竟打印需要涉及到具体的数据问题了。
void SLPrint(SL* ps)
{assert(ps);int i;for (i = 0; i < ps->size; i++) {printf("%d %s %s %d\\n", ps->a[i].sno, ps->a[i].name,ps->a[i].sex, ps->a[i].age);}printf("\\n");
}
顺序表的初始化,相当于int a = 0;实际上这个功能并不是很必要,但是为了代码的规范,以及防止一些错误的出现,建议要进行初始化,一个合格的程序员应该具备初始化意识。
void SLInit(SL* ps)
{assert(ps);ps->a = NULL;ps->size = 0;ps->capacity = 0;
}
顺序表的销毁,因为采用的是动态开辟空间的方式,所以需要释放空间,如果不释放空间会造成内存泄露。这里我们需要先释放内存空间,然后再把指向这块内存空间的指针置为NULL,否则可能会出现非法访问的问题。之后的size和capacity也应该跟着置为0,一方面,空间已经销毁了,他具备的数据个数和容量本身就应该没有了。另一方面,防止让人误以为有数据或者有空间而去进行一些非法操作。
void SLDestroy(SL* ps)
{assert(ps);if (ps->a) {free(ps->a);ps->a = NULL;ps->size = ps->capacity = 0;}
}
检查是否需要扩容,只要在进行数据插入时都需要检查是否需要扩容,然后我们这里有头插尾插和在pos位置插入数据,所以单独把这一功能作为一个函数写出来。提高代码的复用,这样就不用每次都写这么一串代码出来。
注意:在动态开辟空间之后一定要检查空间是否开辟成功了,如果不检查的话可能会出现一些不可预料的错误。malloc和realloc开辟空间都会是返回一个指针,只是在开辟失败后返回的是一个空指针,所以我们只需要检查返回值是否是NULL就行了。
void SLCheckCapacity(SL* ps)
{assert(ps);if (ps->size == ps->capacity) {// 判断是没有空间还是空间不够,如果是没有空间就给个初始空间,空间不够,就把空间扩大一倍int newCapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;// 按照数据类型来进行空间扩容SLDataType* tmp = (SLDataType*)realloc(ps->a, newCapacity * sizeof(SLDataType));// 检查空间是否开辟成功if (NULL == tmp){perror("realloc fail");exit(-1);//终止程序}ps->a = tmp;ps->capacity = newCapacity;}
}
顺序表的尾插和尾删,因为空间是动态开辟的,所以应该在插入数据之前使用SLCheckCapacity()来确保空间是能够允许插入一个数据,在数据插入之后元素个数要加一,不然加了和没加没有区别,因为虽然你空间上存在这个数据了,但是你的函数访问不到这个数据。
还有就是在删除数据的时候,我们并不需要真正的删除掉这个数据,而且要删除掉一个数据实际上并不好删。我们要做的只是让我们的程序访问不到已经被删除的数据就行了,也就是只要size减一就可以了。当我们在插入新数据时,如果插入位置是有数据的,这个数据就会被覆盖掉,所以我们删没删这个数据,实际上是没有影响的。就相当于他有一个初始值而已,但是初始值并不是不能被改变的。
void SLPushBack(SL* ps, SLDataType* x)
{assert(ps);SLCheckCapacity(ps);ps->a[ps->size] = *x;ps->size++;
}void SLPopBack(SL* ps)
{assert(ps);assert(ps->size > 0);ps->size--;
}
顺序表的头插和头删,这里和尾插尾删的区别不是很多,不过有一个点需要注意的是,头插和头删需要移动数据。头插时需要将数据挨着向后移动一位,然后在头部插入数据。头删时,也是采用的覆盖,数据向前移动覆盖掉前一个数据就行了。
void SLPushFront(SL* ps, SLDataType* x)
{assert(ps);// 在数据插入前检查是否需要扩容SLCheckCapacity(ps);int end = ps->size - 1;while (end >= 0) {ps->a[end + 1] = ps->a[end];end--;}ps->a[0] = *x;ps->size++;
}void SLPopFront(SL* ps)
{assert(ps);assert(ps->size > 0);int begin = 0;while (begin < ps->size - 1) {ps->a[begin] = ps->a[begin + 1];begin++;}ps->size--;
}
在pos插入或者删除数据,这组函数的功能实际上是包括头插头删、尾插尾删的,可以用这组函数分别头插头删、尾插尾删。
void SLInsert(SL* ps, int pos, SLDataType* x)
{assert(ps);assert(pos >= 0);assert(pos <= ps->size);// 在数据插入前检查是否需要扩容SLCheckCapacity(ps);int end = ps->size - 1;while (end >= pos) {ps->a[end + 1] = ps->a[end];end--;}ps->a[pos] = *x;ps->size++;
}void SLErase(SL* ps, int pos)
{assert(ps);assert(ps->size >= pos);int ret = pos;while (ret < ps->size - 1) {ps->a[ret] = ps->a[ret + 1];ret++;}ps->size--;
}
查找有多种方式,如顺序查找、二分查找这些,也可以按学号、姓名、年龄这些的来查找,我这里只是写了一个按学号进行的顺序查找。
// 查找数据
int SLFind(SL* ps, int x)
{return Find(ps, x);
}int Find(SL* ps, int sno)
{int i = 0;while (i < ps->size) {if (ps->a[i].sno == sno) return i;i++;}return -1;
}