您的位置:首页 > 编程语言 > C语言/C++

C语言链表实现简单的学生信息管理系统

2019-07-02 11:56 597 查看
版权声明:本文为博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。 本文链接:https://blog.csdn.net/qq_39778055/article/details/94414424

第一次写博客,今天学习了C语言链表的相关知识,自己实现了一个很简单的学生成绩管理系统,同时也温习了一下多文件编程,想和大家分享一下自己从中的一些经验和感受。

 

头文件

[code]//List.h 包含结构体的定义
#ifndef M	//使用条件编译来避免重复包含
#define M
struct Student
{
char name[50];//学生的姓名

double score;//学生的成绩

struct Student *pnext;//存储下一个节点的地址
};
#endif

typedef struct Student ST;//简化结构体的声明
[code]//function.h,包含函数的定义
#define _CRT_SECURE_NO_WARNINGS//关闭安全检查
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include"List.h"

void add(ST **head, char *name, double iscore);//增加学生的成绩信息

void del(ST **head, char *name, double iscore);//删除学生的成绩信息

void get(const ST *head, char *name);//查询学生的成绩信息

void change(ST *head, char *name, double score, double sscore);//改变学生的成绩信息

void sort(const ST *head);//对学生的成绩进行排序

ST  *rev(ST *head);//逆转所有学生的成绩信息

void insert(ST **head, char *name, double iscore);//插入学生的成绩信息

void print(const ST *head);//打印学生的成绩信息

ST *delall(ST *head);//删除所有学生的信息

//各个函数的实现

[code]#include"function.h"

//实现add函数

void add(ST **head, char *name, double iscore)//增加学生的成绩信息
{
if (*head == NULL)//整个链表为空
{
ST *phead = (ST *)malloc(sizeof(ST));//动态分配内存空间

strcpy(phead->name, name);//字符串拷贝
phead->score = iscore;
phead->pnext = NULL;//最后一个节点的指针域为NULL

*head = phead;//指向头节点
}
else//链表为非空
{
ST *p = *head;//存储首节点的地址

while (p->pnext)//循环到最后一个节点,并且不改变传入的头节点指针的指向
{
p = p->pnext;
}

ST *pnew = (ST *)malloc(sizeof(ST));

strcpy(pnew->name, name);
pnew->score = iscore;
pnew->pnext = NULL;//最后一个指针域为空

p->pnext = pnew;//将节点连接起来
}

}

由于函数的副本机制,所以改变一个指针的指向需要使用二级指针

[code]#include"function.h"

//实现del函数

void del(ST **head, char *name, double iscore)//删除学生的成绩信息
{
ST *p = *head;

if ((strcmp(p->name, name) == 0) && (p->score == iscore))//删除的为首节点
{
*head = p->pnext;//指向下一个节点

free(p);//释放内存
}
else//删除的不是首节点
{
ST *p1 = p;//记录上一个节点的地址

while (p->pnext)
{

if ((strcmp(p->name, name) == 0) && (p->score == iscore))//找到了待删除的节点
{

p1->pnext = p->pnext;//删除节点p

free(p);

break;//跳出循环
}

p1 = p;

p = p->pnext;//下一次的循环
}
}
}

删除链表的一个节点只需让上一个节点存储该节点的指针域即可

[code]#include"function.h"

//实现get函数

void get(const ST *head, char *name)
{
ST *p = head;

while (p->pnext)
{
if (strcmp(p->name, name) == 0)
{
printf("%s的成绩为%lf\n", name, p->score);
}

p = p->pnext;
}
}
[code]#include"function.h"

//实现change函数

void change(ST *head, char *name, double score, double sscore)
{
ST *p = head;

while (p->pnext)
{
if ((strcmp(p->name, name) == 0) && (p->score == score))
{
p->score = sscore;//函数的副本机制,必须通过指针才能改变原来的值

break;
}

p = p->pnext;
}
}

 

[code]#include"function.h"

//实现sort函数

void sort(const ST *head)
{
//选择排序法
for (ST *pi = head; pi; pi = pi->pnext)
{
for (ST *pj = pi->pnext; pj; pj = pj->pnext)
{
if ((pi->score) > (pj->score))
{
//交换成绩
double temp = pi->score;
pi->score = pj->score;
pj->score = temp;

//交换姓名
char str[50];
strcpy(str, pi->name);
strcpy(pi->name, pj->name);
strcpy(pj->name, str);
}
}

}

}
[code]#include"function.h"

//实现rev函数

ST * rev(ST *head)//链表的逆转
{
if (head == NULL || head->pnext == NULL)//链表为空或者只有一个节点
{
return head;
}

ST *p1 = head, *p2 = NULL, *p3 = NULL;

p2 = p1->pnext;//指向下一个节点

while (p2)//p2为NULL时循环结束
{
p3 = p2->pnext;

p2->pnext = p1;//存储前一个节点的值

p1 = p2;

p2 = p3;//指针依次向后移动一位

}

head->pnext = NULL;//最后一个指针域为NULL

head = p1;//指向头节点

return head;//返回头节点,由于函数有副本机制,因此只有通过返回值
//并且在main函数中赋值才能在不使用二级指针的情况下改变头节点

}

链表的逆转就是让每一个节点依次存储前一个节点的地址,最后再让头指针指向之前的最后一个节点即可。

[code]#include"function.h"

//实现insert函数

static void inserth(ST **head, char *name, double iscore)//在链表的头部插入
{
ST *p = (ST *)malloc(sizeof(ST));

strcpy(p->name, name);
p->score = iscore;
p->pnext = *head;//将节点连接起来

*head = p;//指向头节点
}

static void insertc(const ST *head, ST *phead, ST *pback, char *name, double iscore)//在链表的中间插入
{
ST *p = (ST *)malloc(sizeof(ST));

strcpy(p->name, name);
p->score = iscore;
phead->pnext = p;
p->pnext = pback;//将链表连接起来

}

void insert(ST **head, char *name, double iscore)//实现节点的插入
{
ST *p = *head;

if (iscore < (p->score))//比第一个成绩还小,则在首节点前面插入
{
inserth(head, name, iscore);
}
else//在中间或者尾部插入
{
ST *pc = NULL;//记录当前节点的位置

//定位到待插入点的位置
while (((p->score) < iscore) && p->pnext)
{
pc = p;

p = p->pnext;
}
if (p->pnext == NULL)//尾部插入
{
add(head, name, iscore);
}
else//中间插入
{
insertc(*head, pc, pc->pnext, name, iscore);
}

}
}

 链表在插入之前需要先进行排序操作

[code]#include"function.h"

//实现print函数

void print(const ST *head)//输出所有节点
{
ST *p = head;

while (p)
{
printf("学生%s的成绩为%lf\n", p->name, p->score);

p = p->pnext;
}
}
[code]#include"function.h"

//实现delall函数

ST *delall(ST *head)//删除所有节点
{
ST *p1 = NULL;

while (head->pnext)
{
p1 = head->pnext;

head->pnext = p1->pnext;//删除节点p2

free(p1);//释放p1的内存

//printf("\n删除后的链表为:\n\n");

//print(head);//输出删除后的结果

}

free(head);//删除第一个节点

return NULL;//返回空指针

}

删除整个链表时,保持头节点的位置不变,依次删除下一个节点,最后再删除头节点即可。

[code]#include"function.h"

void main()
{
ST *head = NULL;

int num;

printf("请输入学生的人数:\n");
scanf("%d", &num);//从键盘获取num的输入

int count = 1;//计数

while (num)
{
char name[50];

double score = 0;

printf("请输入第%d个学生的姓名:\n", count);

scanf("%s", name);//从键盘获取输入

printf("请输入第%d个学生的成绩:\n", count);

scanf("%lf", &score);

add(&head, name, score);

count++;

num--;
}

sort(head);

printf("\n\n学生的成绩为:\n");

print(head);

system("pause");

}

最后是main函数中进行调用,由于已经直接包含了头文件,所以此时没必要再用extern进行外部函数声明。

通过这个简单的小项目,也进一步巩固了自己对链表的增删查改,逆转等操作的熟悉,同时也复习了函数副本以及二级指针的相关知识!

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: