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

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

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

73a51be2e9d345069d78cbf564f2bee6.png

下面是具体代码实现。


SList.h文件

 #pragma once防止库函数的重复引用,因为库函数会在预编译的时候在程序中展开,会增大程序的体积。

 通过typedef对数据重命名,之后需要修改数据就十分方便。并且其他函数不需要太多的改动。

 需要注意的是,这里结构体传的是指针,减少没必要的内存消耗。

#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
typedef struct student {
  char name[20];
  char sex[5];
  int sno;
  int age;
}SLTDataType;
typedef struct SListNode {
  SLTDataType data;
  struct SListNode* next;
}SListNode;
//动态申请一个节点
SListNode* BuySListNode(SLTDataType* data);
//单链表打印
void SListPrint(SListNode* plist);
//创建单链表
//SListNode* CreateSListNode(int x);
//单链表销毁
void SListDestroy(SListNode** pplist);
//尾插尾删
void SListPushBack(SListNode** pplist, SLTDataType* x);
void SListPopBack(SListNode** pplist);
//头插头删
void SListPushFront(SListNode** pplist, SLTDataType* x);
void SListPopFront(SListNode** pplist);
//查找
SListNode* SlistFind(SListNode* pos, int sno);
//在pos位置插入删除
void SListInsertAfter(SListNode* pos, SLTDataType* x);
void SListEraseAfter(SListNode* pos);

main.c文件

  因为重点在于数据结构链表的使用,所以直接给定一些数据,就不进行重复繁琐的数据输入工作了。

#include "SList.h"
void testSList() {
  SLTDataType stu1 = { "张三", "男", 110701, 22 };
  SLTDataType stu2 = { "李四", "男", 110702, 21 };
  SLTDataType stu3 = { "王五", "女", 110703, 23 };
  SLTDataType stu4 = { "赵六", "女", 110704, 22 };
  SLTDataType stu5 = { "周七", "男", 110705, 23 };
  SListNode* plist = NULL;
  SListPushBack(&plist, &stu1);
  SListPushBack(&plist, &stu2);
  SListPushBack(&plist, &stu3);
  SListPrint(plist);
  SListPopBack(&plist);
  SListPrint(plist);
  SListPushFront(&plist, &stu4);
  SListPushFront(&plist, &stu5);
  SListPrint(plist);
  SListPopFront(&plist);
  SListPrint(plist);
  SListNode* node = SlistFind(plist, 110701);
  printf("%s %d %s %d\n", node->data.name, node->data.sno, node->data.sex, node->data.age);
  SListInsertAfter(node, &stu3);
  SListPrint(plist);
  SListEraseAfter(node);
  SListPrint(plist);
  SListDestroy(&plist);
}
int main() {
  testSList();
  return 0;
}

SList.c文件

  打印函数的实现,如果链表中的数据类型发生了改变,其他功能函数基本上不需要有什么变化, 打印函数对应修改一下就行了,毕竟打印需要涉及到具体的数据问题了。

void SListPrint(SListNode* phead) {
  SListNode* cur = phead;
  while (cur != NULL) {
    printf("%s %d %s %d\n", cur->data.name, cur->data.sno, cur->data.sex, cur->data.age);
    cur = cur->next;
  }
  printf("NULL\n");
}

动态申请一个节点,链表不像顺序表。链表在逻辑上是连续的,但是在空间上不一定是连续的。并且一个节点存储一个数据,所以只需要按需开辟空间节点就可以了,没有必要通过初始化来申请一定的空间。

SListNode* BuySListNode(SLTDataType* data) {
  SListNode* newnode = (SListNode*)malloc(sizeof(SListNode));
  if (newnode == NULL) {
    perror("malloc fail");
    exit(-1);
  }
  newnode->data = *data;
  newnode->next = NULL; 
  return newnode;
}

链表的销毁,链表的节点也是采用动态开辟空间的方式开辟的,所以也需要释放空间,否则会造成内存泄露。因为每个节点都是单独开辟的空间,所以需要针对每一个节点进行释放。从第一个节点开始,一个一个的释放掉,然后把指针移动到下一个节点。直到最后一个空间释放掉,然后返回一个空指针。

void SListDestroy(SListNode** pplist) {
  SListNode* cur = *pplist;
  while (cur) {
    SListNode* next = cur->next;
    free(cur);
    cur = next;
  }
  *pplist = NULL;
}

尾插尾删的功能实现。每一次尾插都需要开辟一个链表节点,这也是为什么开辟节点需要单独写一个函数的原因,因为还有头插和在pos位置插入。在插入之前我们需要检查一下这个链表指针是否是空值,如果是空值则说明,这个数据是第一个数据,开辟的节点空间也是第一个节点,就返回这个节点。如果不是空值则说明这个链表是有值的,我们需要找到当前链表的最后一个节点,然后把数据的节点插入到最后一个节点后面。

 尾删则需要找到链表的最后一个节点,释放掉这块空间,然后把倒数第二个节点的next指针置为空。如果不把最后一个节点释放掉也会造成内存泄露。

void SListPushBack(SListNode** pplist, SLTDataType* x) {
  SListNode* newnode = BuySListNode(x);
  if (*pplist == NULL) {
    *pplist = newnode;
  }
  else {
    SListNode* ptail = *pplist;
    while (ptail->next != NULL) {
      ptail = ptail->next;
    }
    ptail->next = newnode;
  }
}
void SListPopBack(SListNode** pplist) {
  assert(*pplist);
  SListNode* ptail = *pplist;
  while (ptail->next->next != NULL) {
    ptail = ptail->next;
  }
  free(ptail->next);
  ptail->next = NULL;
}

头插头删的函数实现,实现与尾插尾删相似。不过头插时不需要遍历找到最后一个节点,然后进行插入。只需要把新开辟节点的next指针指向当前链表的头结点,然后把新开辟的节点置为头结点就行了。

 头删就更简单了,释放掉头结点的空间,然后把第二个节点置为头结点就行了。

void SListPushFront(SListNode** pplist, SLTDataType* x) {
  SListNode* newnode = BuySListNode(x);
  newnode->next = *pplist;
  *pplist = newnode;
}
void SListPopFront(SListNode** pplist) {
  assert(*pplist);
  SListNode* next = (*pplist)->next;
  free(*pplist);
  *pplist = next;
}

 在pos插入或者删除数据,这组函数的功能实际上是包括头插头删、尾插尾删的,可以用这组函数分别头插头删、尾插尾删。

void SListInsertAfter(SListNode* pos, SLTDataType* x) {
  assert(pos);
  SListNode* newnode = BuySListNode(x);
  newnode->next = pos->next;
  pos->next = newnode;
}
void SListEraseAfter(SListNode* pos) {
  assert(pos);
  SListNode* cur = pos->next->next;
  pos->next = cur;
}


  查找有多种方式,如顺序查找、二分查找这些,也可以按学号、姓名、年龄这些的来查找,我这里只是写了一个按学号进行的顺序查找。

SListNode* SlistFind(SListNode* pos, int sno) {
  SListNode* cur = pos;
  while (cur) {
    if (cur->data.sno == sno) {
      return cur;
    }
    cur = cur->next;
  }
  return NULL;
}
目录
相关文章
|
7月前
|
C语言
对链表使用插入排序的C语言实现示例
对链表使用插入排序的C语言实现示例
|
7月前
|
C语言
基于链表实现的链式管理系统(C语言课设)
基于链表实现的链式管理系统(C语言课设)
|
1月前
|
存储 C语言
【数据结构】手把手教你单链表(c语言)(附源码)
本文介绍了单链表的基本概念、结构定义及其实现方法。单链表是一种内存地址不连续但逻辑顺序连续的数据结构,每个节点包含数据域和指针域。文章详细讲解了单链表的常见操作,如头插、尾插、头删、尾删、查找、指定位置插入和删除等,并提供了完整的C语言代码示例。通过学习单链表,可以更好地理解数据结构的底层逻辑,提高编程能力。
86 4
|
2月前
|
C语言
无头链表再封装方式实现 (C语言描述)
如何在C语言中实现无头链表的再封装,包括创建节点和链表、插入和删除操作、查找和打印链表以及销毁链表的函数。
29 0
|
2月前
|
测试技术 C语言
单链表之无头链表(C语言版)
本文详细介绍了使用C语言实现无头单链表的方法,包括节点和链表结构的定义、链表的创建与销毁、节点的插入与删除,以及链表的打印等功能。文章通过具体的代码示例,展示了如何在无头链表中进行头插法、尾插法、自定义位置插入和删除,以及如何清空和销毁链表。
47 0
单链表之无头链表(C语言版)
|
1月前
|
C语言
【数据结构】双向带头循环链表(c语言)(附源码)
本文介绍了双向带头循环链表的概念和实现。双向带头循环链表具有三个关键点:双向、带头和循环。与单链表相比,它的头插、尾插、头删、尾删等操作的时间复杂度均为O(1),提高了运行效率。文章详细讲解了链表的结构定义、方法声明和实现,包括创建新节点、初始化、打印、判断是否为空、插入和删除节点等操作。最后提供了完整的代码示例。
59 0
|
2月前
|
C语言
无头链表二级指针方式实现(C语言描述)
本文介绍了如何在C语言中使用二级指针实现无头链表,并提供了创建节点、插入、删除、查找、销毁链表等操作的函数实现,以及一个示例程序来演示这些操作。
36 0
|
5月前
|
存储 数据管理 C语言
C语言实战 | 使用链表完成“贪吃蛇”游戏
【7月更文挑战第1天】整体思维,即系统思维,强调以整体视角理解事物。在编程中,结构体体现这种思想,将相关变量打包处理。示例展示了如何用链表而非数组实现“贪吃蛇”游戏,链表提供了更灵活的动态数据管理。一系列代码图片详细描绘了链表结构体在游戏中的应用,包括节点定义、移动、碰撞检测等,凸显了使用链表的优势和代码的清晰组织。
61 0
C语言实战 | 使用链表完成“贪吃蛇”游戏
|
6月前
|
存储
数据结构——双向链表(C语言版)
数据结构——双向链表(C语言版)
34 2
|
6月前
|
算法 C语言
数据结构——单向链表(C语言版)
数据结构——单向链表(C语言版)
54 2