本次在实践周中,学会了一些新东西,虽然这些新东西可能在未来并没有什么卵用,但是只要是能用到的,以后指不定就用上了。因为是临时研究没所以有的地方我也不是很清楚,以后再说吧。
单链表
首先是单链表,单链表说实话是c语言数据结构与算法的东西,原本并没有学过,因此只能先写写我的理解。
typedef struct student {
char name[30];// 学生姓名
char num[30];//学号
char zy[5];//专业
int bj;//班级
int math;//数学成绩
int yy;//英语成绩
int dl;//专业课导论成绩
int cyy;//C语言成绩
int ty;//体育成绩
int s;//总分
double pj;//平均分
}Student;
typedef struct Node {
Student stu;
struct Node* next;
} Node;
Node* head = NULL;
这是我们定义的结构体,单链表说实话就是结构体变量和结构变量的连接,把一个结构体看成两个部分,一个是,data,另一个是,next,也就是下一个指针域。说实话目前还不是很懂,还有一个就是需要定义一个全局变量 Node* head = NULL;
首先是指针和结构体变量的区别,指针就是 struct Node* next,这个东西叫做指针,而后 Node* stu = (Node*)malloc(sizeof(Node)) ; 这个东西叫做动态内存分配,有了这个东西,一个指针才能变成结构体变量。,其实这个东西就叫作,创建一个新的结点。
只有变成了结构体变量,才能对其进行赋值 (好像后面也有遇到一些没有赋值,但是同样可以传递变量的代码,不懂,先这样理解吧)
单链表——头插法
头插法就是通过建立一个新的结点,然后不断将head指针指向新的节点,就可以插入,代码很简单明了。
单链表——尾插法
尾插法就是跟头插法相反,但是相对来说也复杂,首先就是可以看到,尾插法需要将新的节点的下一个指针域始终指向 NULL,然后就开始了分类讨论,如果一开始的数据是第一个数据,则将 head 指针指向第一个数据,然后第一个数据的 next 指向 NULL。然后就是第二个数据,第二个数据的画没有进入 while 循环,而是
直接再定义一个指针 lastnode 代替 head ,值得注意的是,在单链表中,head指针是不能随便乱动的,它的作用是指向头节点的位置。之后,让lastNode的指针指向下一个,如果下一个节点的next为空的话。这是两个数据的情况下,接下来是第三个数据,如果出现了第三个数据,也就是说,lastnode又再次回到了头结点的时候,然后进入了while循环判断,如果下一跳不为NULL,则一直跳到最后一位,然后让lastnode的next与新的结点进行拼接。
总结来说就是根据新的结点数量,然后不断地循环后移指针到最后一位,最后拼上新的结点,就可以了。
单链表——删除
单链表的删除比较特殊,同样是判断个数,首先是如果只有一个数据的时候,定义一个head同样指向NULL,只有一个数据的时候就很好理解,同样就是让head指针转移到newnode身上去,然后直接free()它就可以了。
接下来就来看当有两个数据的时候,也是非常的简单啊,直接让它跳到最后一位,然后删除掉即可。
后面就是有多个数据的时候,比较复杂一点,当我们利用两个条件判断可以删除掉头尾的时候,那我们现在只需要向办法删除掉中间的数据,首先就是定义再定义一个指针 pre ,pre 的作用是定位,定位到要删除的结点的下一位,而temp就是要定位到被删除的结点,然后我们再删除temp,就做到了连接 10 和 30 的情况下,还能删除20。
单链表——搜索
搜索这一个功能就很简单了,我们先定义一个头指针,然后与开始判断指针指向的内容是否与我们所要查找的内容一致,如果一致,则进行输出,如果不一致,则眺下一位。
单链表——插入排序
原谅我的菜鸡,导致我无法学会所有的排序,所以目前只学会了一个插入排序。
//插入排序
void InsertSort(Linklist *head){
if(!head->next) return;
Linklist *node = head->next, *nex = node->next, *pre;
node->next = NULL; //单个节点形成初始有序链表
node = nex;
while(node){
pre = head; //pre在有序链表中找到何时插入位置
nex = node->next; //暂存下一个待排序节点
//找到何时插入位置,pre指向有序链表中最后一个小于node的节点
while(pre->next && pre->next->data < node->data)
pre = pre->next;
//表中插入
node->next = pre->next;
pre->next = node;
node = nex; //node指向下一个待排序节点
}
}
感觉最难的就是排序,妈的老是看不懂。
首先我们要知道的一个东西就是,我们所要定位的头指针再最刚开始的时候,指向的是所要排序数据的前一位,因此 head 才会在 4 之前,
然后就是核心排序思想,首先就是需要将遗传数据分成两段,第一段就是直接将第一个数据跟链表断开,然后开始将断开的这一个数据,和后面的数据进行比对,遇到大的就插后面,遇到小的就插前面。
说的简单点,这个排序的逻辑就是,将一个数据的第一个数据和后面的数据段拆开,然后将数据一个一个进行比对插入
代码:学生成绩管理系统
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<windows.h>
#define M 1000
//***********结构体*****
typedef struct student {
char name[30];// 学生姓名
char num[30];//学号
char zy[5];//专业
int bj;//班级
int math;//数学成绩
int yy;//英语成绩
int dl;//专业课导论成绩
int cyy;//C语言成绩
int ty;//体育成绩
int s;//总分
double pj;//平均分
}Student;
typedef struct Node {
Student stu;
struct Node* next;
} Node;
Node* head = NULL;
//**********开始函数声明*************
void fileread(struct student stu[M]);//*读取文件函数
void filesave(struct student stu[M]);//*保存文件函数
void input(struct student stu[M]);//****输入数据函数
void output(struct student stu[M]);//***输出函数
void del(struct student stu[M]);//******删除学生信息函数 (1 、2)
void gai(struct student stu[M]);//******改成绩函数
void chakang(struct student stu[M]);//**查询学生成绩信息函数
void name_(struct student stu[M]);//****使用学生姓名删其信息 1
void num_(struct student stu[M]);//*****使用学生学号删其信息 2
void order(struct student stu[M]);//****进行排序函数
//***************************************
//输入学生成绩信息的个数,一开始为零
//这个t是进行order排序时用的
int main()
{
Node* stu = (Node*)malloc(sizeof(Node));
int sum = 0, xuan;//xuan一开始进入界面进行选择 ,
system("mode con: cols=200 lines=100"); //控制编译器高和宽
//**********代码运行界面*********
printf("\t\t\t\t 欢迎使用学生成绩管理信息系统\n");
do {
printf("\t\t\t -------------------------------------------\n");
printf("\t\t\t\t ***学生成绩信息管理系统***\n");
printf("\t\t\t\t\t ***************\n");
printf("\t\t\t\t\t 1、输入学生信息\n");
printf("\t\t\t\t\t 2、浏览学生信息\n");
printf("\t\t\t\t\t 3、查看学生信息\n");
printf("\t\t\t\t\t 4、修改学生成绩\n");
printf("\t\t\t\t\t 5、删除学生信息\n");
printf("\t\t\t\t\t 6、学生信息排序\n");
printf("\t\t\t\t\t 7、读取文件信息\n"); //从文件读取
printf("\t\t\t\t\t 8、保存到文件\n"); //保存到文件
printf("\t\t\t\t\t 9、退出系统\n");
printf("\t\t\t\t\t ***************\n");
printf("\t\t\t\t\t 请输入你的选择\n");
scanf("%d", &xuan); //进行选择功能
fflush(stdin); //清除输入缓冲区
if (xuan > 9 || xuan <= 0)
{
sum++;
if (sum >= 8)
{
printf("输入错误次数过8次,程序将重新开始\n");
//通过限制错误输入,避免进入死胡同
system("pause"); //程序暂停,重新选择
system("cls"); //清屏语句
}
}
switch (xuan) //根据选择,调用不同的函数来完成不同的任务
{
case 1:input(stu); break; //输入
case 2:output(stu); break; //输出
case 3:chakang(stu); break; //查询
case 4:gai(stu); break; //修改
case 5:del(stu); break; //删除
case 6:order(stu); break; //排序
case 7:fileread(stu); break;//读取文件数据
case 8:filesave(stu); break;//保存文件数据
case 9:printf("使用完毕,请关掉程序\n"); system("pause"); break;
default:printf("无效的选择,请重新输入\n"); break;
}
} while (xuan != 9);
printf("本程序结束 over\n");
return 0;
}
void filesave()//*******保存文件函数*****
{
char option;
printf("请确认是否已经输入或存有学生信息:'y' or 'n' ?\n");
scanf("%s", &option);
while (option = 'y')
{
FILE* file;
file = fopen("1.c", "w");
if (file == NULL) {
printf("打开文件失败。\n");
return;
}
//写入数据
Node* p = head;
while (p != NULL)
{
fwrite(&p->stu, sizeof(Node), 1, file);
p = p->next;
}
//关闭文件
fclose(file);
printf("数据保存成功。\n");
system("pause");
system("cls");
}
}
void fileread()//*****读取文件函数
{
char option;
printf("请确认是否已经输入或存有学生信息:'y' or 'n' ?\n");
scanf("%s", &option);
int ch;
FILE* fp;
if ((fp = fopen("text", "r")) == EOF)
{
printf("达咩\n");
exit(0);
}
ch = fgetc(fp);
while (ch != EOF)
{
putchar(ch);
ch = fgetc(fp);
}
putchar("\n");
fclose(fp);
return 0;
}
void input()
{
system("cls");
// 动态分配内存,并检查内存分配是否成功
Node* NewNode = (Node*)malloc(sizeof(Node));
if (NewNode == NULL) {
printf("内存分配失败!\n");
return;
}
NewNode->next = NULL;
// 如果链表为空,则将NewNode作为头节点
if (head == NULL)
{
head = NewNode;
}
else
{
Node* q = head;
while (q->next != NULL)
{
q = q->next;
}
q->next = NewNode;
}
// 输入学号
printf("请输入要添加的学生信息\n");
printf("请输入学生学号<三位数>\n");
char stuID[4]; // 为了储存三位数和一个空字符
scanf("%3s", stuID); // 限制输入的字符数,避免缓冲区溢出
if (strlen(stuID) == 3) {
strcpy(NewNode->stu.num, stuID); // 将输入的学号复制到NewNode->stu.num
}
else {
printf("学号必须为三位数!\n");
free(NewNode); // 释放NewNode所占内存
system("pause");
return;
}
// 输入学生姓名
printf("输入同学的姓名(字母)\n");
scanf("%s", NewNode->stu.name);
// 输入学生专业代码
printf("输入学生的专业代码:\n1.物联网工程\n2.计算机科学与技术\n3.软件工程\n4.大数据\n");
char* a[4] = { "物联网工程", "计算机科学与技术", "软件工程", "大数据" };
int i;
scanf("%d", &i);
if (i >= 1 && i <= 4) {
strcpy(NewNode->stu.zy, a[i - 1]);
}
else {
printf("输入的专业代码无效!\n");
free(NewNode); // 释放NewNode所占内存
system("pause");
return;
}
// 输入班级号码
printf("输入学生班级号码:\n");
int classNumber;
scanf("%d", &classNumber);
NewNode->stu.bj = classNumber;
// 输入学生成绩
printf("按顺序输入不大于100的学生各科成绩\n");
printf("数学、英语、专业导论课、C语言、体育\n");
int math, yy, dl, cyy, ty;
scanf("%d%d%d%d%d", &math, &yy, &dl, &cyy, &ty);
// 验证成绩是否在有效范围内
if (math >= 0 && math <= 100 && yy >= 0 && yy <= 100 && dl >= 0 && dl <= 100 && cyy >= 0 && cyy <= 100 && ty >= 0 && ty <= 100) {
NewNode->stu.math = math;
NewNode->stu.yy = yy;
NewNode->stu.dl = dl;
NewNode->stu.cyy = cyy;
NewNode->stu.ty = ty;
NewNode->stu.s = math + yy + dl + cyy + ty; // 计算总分
NewNode->stu.pj = NewNode->stu.s / 5.0; // 计算平均分
printf("学生成绩录入成功!\n");
}
else {
printf("输入的成绩必须在0到100之间!\n");
free(NewNode); // 释放NewNode所占内存
system("pause");
return;
}
// 暂停系统以查看信息
system("pause");
}
void output()
{
system("cls");
Node* LastNode = head;
if (LastNode == NULL)
{
printf("目前没有学生信息记录\n");
}
else
{
while (LastNode != NULL)
{
printf("学号:%s\t姓名:%s\t专业:%s\t班级:%d\t数学:%d\t英语:%d\t专业课导论:%d\tC语言:%d\t体育:%d\t总分:%d\t平均分:%.2f\n",
LastNode->stu.num, LastNode->stu.name, LastNode->stu.zy, LastNode->stu.bj, LastNode->stu.math, LastNode->stu.yy, LastNode->stu.dl, LastNode->stu.cyy, LastNode->stu.ty, LastNode->stu.s, LastNode->stu.pj);
LastNode = LastNode->next;
}
}
// 暂停系统以查看信息
system("pause");
}
void chakang() // *****查看函数 ***
{
system("cls");
// 查找学生信息
Node* temp = head;
char stuID[4];
printf("请输入你想查看的同学学号(三位数)\n");
scanf("%3s", stuID);
if (strlen(stuID) != 3) {
printf("学号必须为三位数!\n");
system("pause");
return;
}
// 查找学号匹配的学生
while (temp != NULL)
{
if (strcmp(temp->stu.num, stuID) == 0) // 比较学号是否匹配,因为是字符串型,所以不能直接比较,这里选择用,strcmp函数的结果来判断
{
printf("学号:%s\t姓名:%s\t专业:%s\t班级:%d\t数学:%d\t英语:%d\t专业课导论:%d\tC语言:%d\t体育:%d\t总分:%d\t平均分:%.2f\n",
temp->stu.num, temp->stu.name, temp->stu.zy, temp->stu.bj, temp->stu.math, temp->stu.yy, temp->stu.dl, temp->stu.cyy, temp->stu.ty, temp->stu.s, temp->stu.pj);
system("pause");
return;
}
temp = temp->next;
}
// 没有找到匹配的学生
printf("没有找到学号为%s的学生信息。\n", stuID);
system("pause");
}
void gai() // ***修改成绩函数***
{
system("cls");
// 查找学生信息
char stuID[4];
Node* p = head;//已经开始混乱,这个链表的名字是固定呢还是不固定呢,算了,随便取一个吧
printf("请输入要修改成绩的学生学号(三位数)\n");
scanf("%3s", stuID); // 限制输入的字符数,避免缓冲区溢出
if (strlen(stuID) != 3) {
printf("学号必须为三位数!\n");
system("pause");
return;
}
// 查找学号匹配的学生
while (p != NULL)
{
if (strcmp(p->stu.num, stuID) == 0) // 比较学号是否匹配
{
int option;
int newScore;
printf("修改学生成绩");
printf("选择要修改的课程:\n1. 数学\n2. 英语\n3. 专业导论\n4. C语言\n5. 体育\n");
scanf("%d", &option);
switch (option) {
case 1:
printf("请输入新的数学成绩(0-100):\n");
scanf("%d", &newScore);
if (newScore >= 0 && newScore <= 100) {
p->stu.math = newScore;
}
else {
printf("成绩必须在0到100之间!\n");
system("pause");
return;
}
break;
case 2:
printf("请输入新的英语成绩(0-100):\n");
scanf("%d", &newScore);
if (newScore >= 0 && newScore <= 100) {
p->stu.yy = newScore;
}
else {
printf("成绩必须在0到100之间!\n");
system("pause");
return;
}
break;
case 3:
printf("请输入新的专业导论成绩(0-100):\n");
scanf("%d", &newScore);
if (newScore >= 0 && newScore <= 100) {
p->stu.dl = newScore;
}
else {
printf("成绩必须在0到100之间!\n");
system("pause");
return;
}
break;
case 4:
printf("请输入新的C语言成绩(0-100):\n");
scanf("%d", &newScore);
if (newScore >= 0 && newScore <= 100) {
p->stu.cyy = newScore;
}
else {
printf("成绩必须在0到100之间!\n");
system("pause");
return;
}
break;
case 5:
printf("请输入新的体育成绩(0-100):\n");
scanf("%d", &newScore);
if (newScore >= 0 && newScore <= 100) {
p->stu.ty = newScore;
}
else {
printf("成绩必须在0到100之间!\n");
system("pause");
return;
}
break;
default:
printf("选择无效!\n");
system("pause");
return;
}
// 计算总分和平均分
p->stu.s = p->stu.math + p->stu.yy + p->stu.dl + p->stu.cyy + p->stu.ty;
p->stu.pj = p->stu.s / 5.0;
printf("学生学号为%s的成绩已修改\n", stuID);
system("pause");
return;
}
p = p->next;
}
// 未找到对应的学生
printf("未找到学号为%s的学生信息\n", stuID);
system("pause");
}
//自定义删除函数
void del() {
system("cls");
int choice;
printf("请选择你所要删除的方式:1、学号 2、姓名 3、取消\n");
scanf("%d", &choice);
switch (choice) {
case 1: {
printf("请输入要删除的学生学号(三位数):\n");
char id[4];
scanf("%3s", id);
num_(&head, id);
break;
}
case 2: {
printf("请输入要删除的学生姓名:\n");
char studentname[50];
scanf("%s", studentname);
name_(&head, studentname);
break;
}
case 3:
return;
default:
printf("选择无效!\n");
break;
}
system("pause");
}
// 通过学号删除学生信息函数
void num_(Node** head, char* num) {
Node* p = *head;
Node* temp;
if (p == NULL) {
printf("目前没有学生信息\n");
return;
}
// 删除头节点
if (strcmp(p->stu.num, num) == 0) {
temp = p;
*head = p->next;
free(temp);
printf("学号为%s的学生信息已删除\n", num);
return;
}
// 查找并删除目标节点
Node* q = p;
while (q->next != NULL) {
if (strcmp(q->next->stu.num, num) == 0) {
temp = q->next;
q->next = q->next->next;
free(temp);
printf("学号为%s的学生信息已删除\n", num);
return;
}
q = q->next;
}
printf("没有学号为%s的学生信息\n", num);
}
// 通过姓名删除学生信息函数
void name_(Node** head, char* name) {
Node* p = *head;
Node* temp;
if (p == NULL) {
printf("目前没有学生信息\n");
return;
}
// 删除头节点
if (strcmp(p->stu.name, name) == 0) {
temp = p;
*head = p->next;
free(temp);
printf("姓名为%s的学生信息已删除\n", name);
return;
}
// 查找并删除目标节点
Node* q = p;
while (q->next != NULL) {
if (strcmp(q->next->stu.name, name) == 0) {
temp = q->next;
q->next = q->next->next;
free(temp);
printf("姓名为%s的学生信息已删除\n", name);
return;
}
q = q->next;
}
printf("没有姓名为%s的学生信息\n", name);
}
void order() //排序函数
{
printf("请输入你想要进行排序的方式(所有排序的方式均是降序)!!!\n1、数学\t2、英语\t3、物联网导论\t4、C语言\t5、体育\t6、平均分\t7、学号\t8、专业序号排名\n");
int choice;
Node* p = head;
Node* node = head->next;
Node* nex=node->next;
Node*pre;
if (p == NULL)
{
printf("无法排序");
return;
}
if (p->next == NULL)
{
printf("仅有一个数据,无需排列");
return;
}
while (node->next != NULL)
{
scanf("%d", &choice);
if (choice == 1)
{
node->next = NULL; //单个节点形成初始有序链表
node = nex;
while (node) {
pre = p; //pre在有序链表中找到何时插入位置
nex = node->next; //暂存下一个待排序节点
//找到何时插入位置,pre指向有序链表中最后一个小于node的节点
while (pre->next && pre->next->stu.math < node->stu.math)
pre = pre->next;
//表中插入
node->next = pre->next;
pre->next = node;
node = nex; //node指向下一个待排序节点
}
}
else if (choice == 2)
{
node->next = NULL; //单个节点形成初始有序链表
node = nex;
while (node) {
pre = p; //pre在有序链表中找到何时插入位置
nex = node->next; //暂存下一个待排序节点
//找到何时插入位置,pre指向有序链表中最后一个小于node的节点
while (pre->next && pre->next->stu.yy < node->stu.yy)
pre = pre->next;
//表中插入
node->next = pre->next;
pre->next = node;
node = nex; //node指向下一个待排序节点
}
}
else if (choice == 3)
{
node->next = NULL; //单个节点形成初始有序链表
node = nex;
while (node) {
pre = p; //pre在有序链表中找到何时插入位置
nex = node->next; //暂存下一个待排序节点
//找到何时插入位置,pre指向有序链表中最后一个小于node的节点
while (pre->next && pre->next->stu.dl < node->stu.dl)
pre = pre->next;
//表中插入
node->next = pre->next;
pre->next = node;
node = nex; //node指向下一个待排序节点
}
}
else if (choice == 4)
{
node->next = NULL; //单个节点形成初始有序链表
node = nex;
while (node) {
pre = p; //pre在有序链表中找到何时插入位置
nex = node->next; //暂存下一个待排序节点
//找到何时插入位置,pre指向有序链表中最后一个小于node的节点
while (pre->next && pre->next->stu.cyy < node->stu.cyy)
pre = pre->next;
//表中插入
node->next = pre->next;
pre->next = node;
node = nex; //node指向下一个待排序节点
}
}
else if (choice == 5)
{
node->next = NULL; //单个节点形成初始有序链表
node = nex;
while (node) {
pre = p; //pre在有序链表中找到何时插入位置
nex = node->next; //暂存下一个待排序节点
//找到何时插入位置,pre指向有序链表中最后一个小于node的节点
while (pre->next && pre->next->stu.ty< node->stu.ty)
pre = pre->next;
//表中插入
node->next = pre->next;
pre->next = node;
node = nex; //node指向下一个待排序节点
}
}
else if (choice == 6)
{
node->next = NULL; //单个节点形成初始有序链表
node = nex;
while (node) {
pre = p; //pre在有序链表中找到何时插入位置
nex = node->next; //暂存下一个待排序节点
//找到何时插入位置,pre指向有序链表中最后一个小于node的节点
pre->stu.pj = (pre->stu.math + pre->stu.yy + pre->stu.dl + pre->stu.cyy + pre->stu.ty) / 5;
node->stu.pj = (node->stu.math + node->stu.yy + node->stu.dl + node->stu.cyy + node->stu.ty) / 5;
while (pre->next && pre->next->stu.pj < node->stu.pj)
pre = pre->next;
//表中插入
node->next = pre->next;
pre->next = node;
node = nex; //node指向下一个待排序节点
}
}
else if (choice == 7)
{
node->next = NULL; //单个节点形成初始有序链表
node = nex;
while (node) {
pre = p; //pre在有序链表中找到何时插入位置
nex = node->next; //暂存下一个待排序节点
//找到何时插入位置,pre指向有序链表中最后一个小于node的节点
while (pre->next && pre->next->stu.num < node->stu.num)
pre = pre->next;
//表中插入
node->next = pre->next;
pre->next = node;
node = nex; //node指向下一个待排序节点
}
}
else if (choice == 8)
{
node->next = NULL; //单个节点形成初始有序链表
node = nex;
while (node) {
pre = p; //pre在有序链表中找到何时插入位置
nex = node->next; //暂存下一个待排序节点
//找到何时插入位置,pre指向有序链表中最后一个小于node的节点
while (pre->next && pre->next->stu.zy < node->stu.zy)
pre = pre->next;
//表中插入
node->next = pre->next;
pre->next = node;
node = nex; //node指向下一个待排序节点
}
}
else
{
printf("选择无效\n");
}
printf("已重新排序\n");
return;
}
}