赞
踩
对于刚开始接触数据结构,里面的很多知识都很难理解,需要自己多动手敲代码才能提升自己的能力。先简单说说这个学生管理系统,因为考虑到总是会发生改查等功能,并且考虑到资源的合理利用,采用数组或者使用顺序表都不是最佳选(开大了,会造成资源浪费,开小了无法满足正常的使用),所以我们应该使用动态内存,需要用多少我开多少,既不浪费,也能做到持续录入学生信息。
话不多说,上源码:
link.h文件
- typedef int data_t;
- typedef struct student//定义一个结构体,分别存学号,成绩,平均分等
- {
- int num;
- float Chinese;
- float Math;
- float English;
- float Average;
- struct student *next;
- }stu,*stup;
-
-
- stup creat_list();//建立头节点函数
- stup getnode(stup H,data_t x);//返回节点函数。这个函数作用很大,功能是返回x学号的这个学生的节点
- stup freelist(stup H);//释放链表函数。传一个NULL给调用的节点
- void help();//帮助函数
- void clear();//清屏函数
- int Type(stup H);//录入学生函数
- int count(stup H);//计算平均分函数
- int sort(stup H);//排序函数
- int search(stup H);//查找学生函数
- int del(stup H);//删除学生函数
- int List(stup H);//打印学生信息函数
- int listempty();//链表为空打印函数
- int length(stup H);//求链表长度函数
- int arrange(stup H);//整理学生信息函数
- int modify(stup H);//修改学生信息函数
link.c文件
- #include <stdio.h>
- #include"link.h"
- #include <stdlib.h>
-
- void help()//帮助手册函数
- {
- printf("**********************************\n");
- printf("* 学生成绩管理系统——帮助菜单 *\n");
- printf("**********************************\n");
- printf("* H = 显示帮助菜单 *\n");
- printf("* T = 成绩录入 *\n");
- printf("* A = 计算学生平均分 *\n");
- printf("* L = 列出成绩表 *\n");
- printf("* P = 按平均成绩由高到低排序*\n");
- printf("* S = 按学号查询学生成绩 *\n");
- printf("* F = 整理学生表 *\n");
- printf("* X = 删除学生信息 *\n");
- printf("* M = 修改学生信息 *\n");
- printf("* C = 清屏 *\n");
- printf("* Q = 退出程序 *\n");
- printf("**********************************\n");
- printf("* <C> 2023.0809 By 小企鹅 *\n");
- printf("**********************************\n");
- printf("请输入命令=");
- return ;
- }
-
- stup creat_list()//建立头节点函数
- {
- stup H;
- H=(stup)malloc(sizeof(stu));//动态分配节点
- if(H==NULL)
- {
- printf("H malloc is error\n");
- return H ;
- }
- //给定初始值,因为学生学号从1开始,头节点设置全零
- H->num=0;
- H->Chinese=0;
- H->Math=0;
- H->English=0;
- H->Average=0;
- H->next=NULL;//后继设为NULL
- return H;
- }
-
- int Type(stup H)//录入学生函数
- {
- data_t n;
- if(H==NULL) return -1;
- int a;
- float b,c,d,e;
- stup q,p;
- q=H;
- printf("请输入即将录入学生的人数:");
- scanf("%d",&n);
- printf("请输入%d名学生的三門课的成绩:\n",n);
- printf("学号\t语文\t数学\t外语\n");
- for(int i=0;i<n;i++)//循环录入,按照输入的学生人数录入
- {
- p=(stup)malloc(sizeof(stu));//开辟新节点
- if(p==NULL)
- {
- printf("p malloc is error\n");
- return -1;
- }
- scanf("%d%f%f%f",&a,&b,&c,&d);
- //对节点赋值
- p->num=a;
- p->Chinese=b,
- p->Math=c;
- p->English=d;
- p->Average=0;
- p->next=NULL;//节点指向空
- while(q->next)//遍历链表,实现尾部插入
- {
- q=q->next;
- }
- q->next=p;//实现尾部插入
- }
- printf("请输入命令=");
- return 0;
- }
-
- stup getnode(stup H,data_t x)//获取对应学号的节点函数
- {
- if(H==NULL) return NULL;
- if(x<0)
- {
- return NULL;
- }
- if(x==0) return H; //返回H,这个对于删除第一个节点的学生会有使用,不应该返回空。
- stup q;
- q=H;
- int i=0;
- while(i<x)//遍历查找
- {
- q=q->next;
- if(q==NULL)
- {
- return NULL;
-
- }
- i++;
- }
- return q;//返回找到的这个学生的节点
- }
-
- int length(stup H)//求链表长度函数
- {
- if(H==NULL) return -1;
- stup q;
- q=H;
- int sum=0;
- while(q)//遍历链表,获得长度
- {
- sum++;
- q=q->next;
- }
- return sum;//返回长度值
- }
-
- int del(stup H)//删除学生信息函数
- {
- if(H==NULL) return -1;
- if(H->next==NULL)//如果只有一个头节点
- {
- printf("H is empty\n");
- printf("请输入命令=");
- return -1;
- }
- stup q,p,r;
- int x;
- printf("请输入你要删除的学生的学号:");
- scanf("%d",&x);
- if((q=getnode(H,x-1))==NULL)//得到的是前一个的节点
- {
- printf("删除失败,没有对应学号的学生\n");
- printf("请输入命令=");
- return -1;
- }
- if(q->next==NULL)//前驱没有后继,代表这个节点是尾部节点,没有后继,那么删除后继显然不合理
- {
- printf("删除失败,没有对应学号的学生\n");
- printf("请输入命令=");
- return -1;
- }
- p=q->next;//暂时保存这个待删除的节点,你不保存,释放的时候,会找不到这个节点的
- q->next=q->next->next;//重新指向后一个节点
- free(p);//删除之后,释放。
- p=NULL;//置空
- printf("删除成功,输入T命令查或者输入F调整学生表\n");
- printf("请输入命令=");
- return 0;
- }
-
- int List(stup H)//打印学生信息函数
- {
- if(H==NULL) return -1;
- printf("学生成绩如下:\n");
- printf("学号\t语文\t数学\t外语\t平均分\n");
- stup p;
- p=H->next;//指向第一个元素,头节点不需要打印。
- while(p)//遍历p,打印信息
- {
- printf("%d\t%.1f\t%.1f\t%.1f\t%.1f\n",p->num,p->Chinese,p->Math,p->English,p->Average);
- p=p->next;
- }
- printf("请输入命令=");
- return 0;
- }
-
- int listempty()//打印为空函数
- {
- printf("成绩表为空!请先输入命令T录入学生成绩\n");
- printf("请输入命令=");
- return 0;
- }
-
- int arrange(stup H)//整理学生信息函数
- {
- if(H==NULL) return -1;
- stup q=H->next;//指向第一个节点
- int i=1;
- while(q)//遍历链表
- {
- q->num=i++; //重新给学号赋值
- q=q->next;//指针移动
- }
- printf("整理学生表完成,你可以输入命令L查看\n");
- printf("请输入命令=");
- return 0;
- }
-
- int count(stup H)//计算平均分函数
- {
- if(H==NULL) return -1;
- stup q;
- q=H->next;//指向第一个节点
- while(q)//遍历节点
- {
- q->Average=(q->Chinese+q->Math+q->English)/3.0;//求出对应的平均分
- q=q->next;
- }
- printf("平均分已计算。请使用命令L查看。\n");
- printf("请输入命令=");
- return 0;
- }
-
- int sort(stup H)//排序函数,采用插入排序
- {
- if(H==NULL) return -1;
- if(H->next->next==NULL)
- {
- printf("Only one,sort is unnecessary\n");
- printf("请输入命令=");
- return -1;
- }
- stup q,p,r,t;//建立四个节点
-
- p=H->next->next;//指向第二个元素
- q=r=H;//指向第一个元素
- H->next->next=NULL;//断开链表。
-
-
- while(p)//当待插入链表中还有节点
- {
- q=r=H;//每次插入一个后,节点回到初始状态。重新判断插入点
- while(q && p->Average <= q->Average)//当待插入的节点小于该节点,那么我就往后走,因为要保证分数高的在前面,分数低的在后面。
- {
- r=q;//r表示q前驱,因为要插入q节点这个位置,所以保留前一个位置r
- q=q->next;
- }
- t=p->next;//先让后续的链表用t指针保持起来。
- p->next=r->next;//接下来两步实现插入
- r->next=p;
- p=t;//p重新指向下一个节点
- }
- printf("学生成绩已经拍好序,请输入指令L查看\n");
- printf("请输入命令=");
- return 0;
- }
-
- int search(stup H)//查找学生信息函数
- {
- if(H==NULL) return -1;
- int e;
- stup q;
- printf("请输入要查询的学生的学号:");
- scanf("%d",&e);
- if((q=getnode(H,e))==NULL||q==H)//返回这个节点的函数为空,或者为头节点,都为非法数据
- {
- printf("查找失败,没有你要查找的学生\n");
- printf("请输入命令=");
- return -1;
- }
- printf("学号\t语文\t数学\t外语\t平均分\n");
- printf("%d\t%.1f\t%.1f\t%.1f\t%.1f\n",q->num,q->Chinese,q->Math,q->English,q->Average);
- printf("请输入命令=");
- }
-
- void clear()//清屏函数
- {
- system("clear");
- printf("请输入命令=");
- }
-
- int modify(stup H)//修改学生信息函数
- {
- if(H==NULL) return -1;
- stup p;
- int a,n;
- float b,c,d;
- printf("请输入你要修改学生信息的学号:");
- scanf("%d",&n);
- if((p=getnode(H,n))==NULL||p==H)//返回这个节点为空或者为头节点都是非法数据
- {
- printf("没有你要修改信息的学生\n");
- printf("请输入命令=");
- return -1;
- }
- else
- {
- printf("该学生的信息如下所示:\n");//修改之前,先打印这个学生的信息。
- printf("学号\t语文\t数学\t外语\t平均分\n");
- printf("%d\t%.1f\t%.1f\t%.1f\t%.1f\n",p->num,p->Chinese,p->Math,p->English,p->Average);
- printf("请输入你要修改的内容\n");
- printf("学号\t语文\t数学\t外语\n");
- scanf("%d%f%f%f",&a,&b,&c,&d);
- //重新赋值,实现修改
- p->num=a;
- p->Chinese=b,
- p->Math=c;
- p->English=d;
- p->Average=0;
- printf("修改信息成功,输入命令L可查看\n");
- printf("请输入命令=");
- }
- return 0;
- }
-
- stup freelist(stup H)//链表释放函数,返回值为NULL
- {
- if(H==NULL) return NULL;
- stup p;
- while(H)
- {
- p=H;//中间变量
- free(p);//完成释放
- H=H->next;//节点移动
- }
- p=NULL;
- return NULL;//返回给我的main函数的H,因为在哪里它替我保存了这个链表。单独在这这里H=NULL,是不行的,因为这里是局部变量,外面也起不到作用。所以使用这种简单的方式。
- }
test.c文件
- #include<stdio.h>
- #include"link.h"
- #include <stdlib.h>
-
- stup H,q;
- //一块的测试函数,见名知意,我就不赘述了
- void test_Type();
- void test_del();
- void test_count();
- void test_List();
- void test_search();
- void test_arrange();
- void test_sort();
- void test_modify();
- void test_clear();
- void test_help();
-
- int main()
- {
- char c;
- help();
- int d;
- H=creat_list();//建立头节点,H指向这个节点
- if(H==NULL) return -1;
- while(1)//循环函数
- {
- scanf("%c",&c);
- switch(c)
- {
- case 'T' : test_Type();//录入学生信息
- break;
-
- case 'X' : test_del();//删除学生信息
- break;
-
- case 'L' : test_List();//展示学生信息
- break;
-
- case 'S' : test_search();//查找学生
- break;
-
- case 'F' : test_arrange();//调整学生信息
- break;
-
- case 'A' : test_count(); //计算平均分
- break;
-
- case 'P' : test_sort(); //排序
- break;
-
- case 'M' : test_modify(); //修改学生信息
- break;
-
- case 'C' : test_clear();//清屏
- break;
-
- case 'H' : test_help();//查看帮助手册
- break;
-
- case 'Q' : printf("press anything quit\n");//退出
- H=freelist(H);
- q=NULL;
- return -1;
-
- default : printf("input error!once again\n请输入命令=");//其他
- getchar();
- break;
- }
- }
- H=freelist(H);//调用释放函数。
- q=NULL;
- return 0;
- }
- //需要注意的是:对于每次输入都有垃圾字符会留在缓冲区,需要使用getchar()”吃掉“。
- //调用这些删改查函数的时候,都应该先判断链表中节点是否为空,也就是判断有没有学生信息。
- void test_Type()
- {
- Type(H);
- getchar();
- }
- void test_del()
- {
- q=H;
- if(q->next)
- {
- del(H);
- getchar();
- }
- else
- {
- listempty();
- getchar();
- }
- }
- void test_count()
- {
- q=H;
- if(q->next)
- {
- count(H);
- getchar();
- }
- else
- {
- listempty();
- getchar();
- }
- }
- void test_List()
- {
- q=H;
- if(q->next)
- {
- List(H);
- getchar();
- }
- else
- {
- listempty();
- getchar();
- }
- }
- void test_search()
- {
- q=H;
- if(q->next)
- {
- search(H);
- getchar();
- }
- else
- {
- listempty();
- getchar();
- }
- }
- void test_arrange()
- {
- q=H;
- if(q->next)
- {
- arrange(H);
- getchar();
- }
- else
- {
- listempty();
- getchar();
- }
- }
- void test_sort()
- {
- q=H;
- if(q->next)
- {
- sort(H);
- getchar();
- }
- else
- {
- listempty();
- getchar();
- }
- }
- void test_modify()
- {
- q=H;
- if(q->next)
- {
- modify(H);
- getchar();
- }
- else
- {
- listempty();
- getchar();
- }
- }
- void test_clear()
- {
- clear();
- getchar();
- }
- void test_help()
- {
- help();
- getchar();
- }
运行效果截图:
截图过多,我就不一一展示了。对于本个项目,基本的增删改查都实现了,使用简单的链表就可以实现这一功能。给大家说一下思路:
以上是这个项目的简单流程,一些整理学生表的功能我就不赘述了,这个功能一般使用在删除了学生之后的时候,我可以使用这个函数整理一下我的学生表。
最后说说实现这个项目的一些经历和过程吧:
代码实现还是比较简单的,学习数据结构的话,应该多动手画画图,便于理解,在这个过程中,你会对指针的使用更上一步,当然也有很多地方有很多坑,比如指针总是指向一些未知区域,这是在指针移动的时候,你没有做好结束条件的判断,倘若在这之后你还在修改里面的内容的话,那么后果是无法想象的。总之也就是说:
以上是小编初学链表的一些心得与体会。希望对你有所帮助。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。