顺序表实现学生信息管理(C语言)

简介: 顺序表实现学生信息管理(C语言)

用顺序表实现学生信息管理。这里放一下有哪些文件。

87b8d77149e34b5da788e718df632d6f.png


  下面是具体实现的代码。

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;
}
目录
相关文章
|
2月前
|
存储 算法 程序员
【数据结构】C语言实现顺序表万字详解(附完整运行代码)
【数据结构】C语言实现顺序表万字详解(附完整运行代码)
55 0
|
2月前
|
C语言
C语言顺序表
C语言顺序表
43 0
|
9月前
|
存储 测试技术 C语言
C语言实现对顺序表和链表的增删改【数据结构/初阶】
C语言实现对顺序表和链表的增删改【数据结构/初阶】
34 0
|
9月前
|
存储
从零教你拿捏数据结构(顺序表)C语言版
从零教你拿捏数据结构(顺序表)C语言版
32 0
|
1月前
|
存储 C语言
数据结构——顺序表(C语言版)
数据结构——顺序表(C语言版)
28 5
|
2月前
|
存储 C语言
C语言实验-动态顺序表实现简易通讯录(二)
在这个C语言实验中,你将实现一个简单的通讯录,它使用动态顺序表来存储联系人信息。
31 2
|
2月前
|
存储 C语言
数据结构——顺序表(C语言)
数据结构——顺序表(C语言)
19 1
|
2月前
|
存储 C语言
C语言实验-动态顺序表实现简易通讯录(一)
本文介绍了使用C语言模拟实现通讯录的步骤,包括使用动态顺序表存储联系人信息,每个联系人包含姓名、性别、电话和住址。功能包括新增、查找、删除、修改和查看联系人信息,以及按关键词排序。代码示例展示了如何定义联系人结构体、通讯录顺序表,以及主函数中的菜单选择和输入处理。文章还强调了在读取用户输入时避免出现死循环的问题,并提供了初始化和销毁通讯录的函数,以及如何判断和增加通讯录容量的方法。
41 1
|
1月前
|
存储 C语言
数据结构之顺序表(C语言版)
数据结构之顺序表(C语言版)
14 0
|
2月前
|
存储 缓存 程序员
初阶数据结构之---顺序表和链表(C语言)
初阶数据结构之---顺序表和链表(C语言)