前言
初学C语言,请大佬多提宝贵意见,感激不尽。欢迎各位初学者前来交流。
/*
学生信息管理系统:
1.输入工程实验信息
2.显示所有信息
3.根据ID删除指定工程信息
4.根据名称查找工程信息
5.在序号前插入工程信息
6.根据ID插入工程信息
7.根据ID大小进行信息排序
8.根据ID修改信息
9.将信息导出
10.从文件中读取数据
11.删除链表
总结:
1.花了3个小时写完,与上次有进步。
2.不知道自己的 C 语言现在有没有入门,缺乏交流。
3.利用while,可以实现输入错误重新输入的目的。
4.凡是对结点中“数据域”中的“元素”进行修改的,都是用p=head.pNext,也就是p直接指向需要修改的那个结点,具体怎么理解,自己在思考。
5.凡是对链表中完整的结点做出修改的,如删除整个结点,添加结点等,只要打乱原有的排列顺序,都需要记住被修改结点的上一个结点,这样才能建立连续。
6.链表排序:
//选择排序的总体思想是:将已经挑选出来的结点进行删除,再对剩下的结点进行最大值或者最小值筛选。也就意味着,如果原来的链表还有用处,就需要重新创建一个链表,在这个链表上进行排序,再将筛选出的值赋值到第三个链表中。
//对链表进行选择排序法,至少需要两个链表。
7.删除链表。找到下一个结点,找到之后对当前的结点进行释放。
*/
————————————————
版权声明:本文为CSDN博主「sfdely」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/sfdely/article/details/100558748
# include<stdio.h>
# include<stdlib.h>
# include<string.h>
# include<malloc.h>
struct Student
{
char ID[5];
char name[20];
int score;
struct Student *pNext;
};
typedef struct Student STU;
STU head, *tail, *p;
void Input(void);
void Output(void);
void Delete(void);
void Search(void);
void Insert(void);
void Insert_ID(void);
void Sort_ID(void);
void Report(void);
void Putout(void);
void In_read(void);
void Delete_All(void);
int main(void)
{
int n;
while(1)
{
printf("1.输入工程实验信息\n");
printf("2.显示所有信息\n");
printf("3.根据ID删除指定工程信息\n");
printf("4.根据名称查找工程信息\n");
printf("5.在序号前插入工程信息\n");
printf("6.根据ID插入工程信息\n");
printf("7.根据ID大小进行信息排序\n");
printf("8.根据ID修改信息\n");
printf("9.将信息导出\n");
printf("10.从文件中读取数据\n");
printf("11.删除链表\n");
printf("\n请输入需要的功能 : ");
scanf("%d", &n);
switch(n)
{
case 1:Input(); break;
case 2:Output(); break;
case 3:Delete(); break;
case 4:Search(); break;
case 5:Insert(); break;
case 6:Insert_ID(); break;
case 7:Sort_ID(); break;
case 8:Report(); break;
case 9:Putout(); break;
case 10:In_read(); break;
case 11:Delete_All(); break;
}
}
return 0;
}
void Input(void)
{
int n, i;
if(head.pNext != NULL)
{
printf("表格已存在,是否覆盖?否则按1");
scanf("%d", &n);
if(1 == n) //等于判断,要将变量名放在后面,防止手误写成 n=1.
return ;
}
//链表创建
printf("请输入有多少个学生: ");
scanf("%d", &n);
head.pNext = NULL;
tail = &head;
for(i=0; i<n; i++)
{
p = (STU *)malloc(sizeof(STU));
if(NULL == p)
{
printf("链表创建失败\n");
return;
}
p->pNext = NULL;
printf("请输入第%d个实验信息 : ", i+1);
scanf("%s%s%d", p->ID, p->name, &p->score);
tail->pNext = p;
tail = p;
}
return ;
}
//链表输出
void Output(void)
{
int i=1;
p = head.pNext; //重点 !!凡是对结点中“数据域”中的“元素”进行修改的,都是用p=head.pNext,也就是p直接指向需要修改的那个结点,具体怎么理解,自己在思考。读取、输出、修改元素。
if(NULL == head.pNext)
{
printf("表格不存在\n\n");
return ;
}
while(p != NULL)
{
printf("%-10d%-6s%-15s%-4d\n", i, p->ID, p->name, p->score);
p = p->pNext;
i++;
}
printf("\n\n");
return ;
}
//删除结点
void Delete(void)
{
STU *pr;
char ch[20];
int n = 3;
if(NULL == head.pNext)
{
printf("表格不存在\n");
return ;
}
while(3 == n)
{
p = &head; //凡是对链表中完整的结点做出修改的,如删除整个结点,添加结点等,只要打乱原有的排列顺序,都需要记住被修改结点的上一个结点,这样才能建立连续。
pr = p->pNext;
printf("请输入需要删除的ID:");
scanf("%s", ch);
while(pr != NULL)
{
if(strcmp(ch, pr->ID) == 0)
{
p->pNext = pr->pNext;
free(pr);
return ;
}
p = p->pNext;
pr = p->pNext;
}
printf("数据不存在,若要重新输入请按3,否则返回 :");
scanf("%d", &n);
}
return ;
}
//查找指定的结点中元素
void Search(void)
{
char ch[20];
int n=1, k=0;
while(n)
{
p = head.pNext; //p直接指向需要操作的结点即可。
printf("请输入要查询的工程名\n");
scanf("%s", ch);
while(p!=NULL)
{
if(strcmp(ch, p->name) == 0)
{
printf("%-6s%-15s%-4d\n", p->ID, p->name, p->score);
k++;
printf("需要查询其他,请继续输入,返回请按0\n");
scanf("%d", &n);
if(0 == n)
return ;
else break;
}
p = p->pNext;
}
if(k == 0)
{
printf("输入的姓名有误,重新输入按1,返回上一层按其他: ");
scanf("%d", &n);
if(n != 1)
return ;
}
}
return ;
}
//插入结点
void Insert(void)
{
int n, i=0;
int k=1;
STU *pr;
printf("请输入需要在哪个序号前插入数据: ");
scanf("%d", &n);
while(n <= 0)
{
printf("请输入大于0的数 :");
scanf("%d", &n);
}
while(k)
{
p = &head; //p指向被操作的上一个结点,建立联系。
i = 0;
while(p->pNext != NULL)
{
i++;
if(i == n) //用i判断是第几个序列。
{
pr = (STU *)malloc(sizeof(STU));
printf("请输入需要插入的数据: ");
scanf("%s%s%d", pr->ID, pr->name, &pr->score);
pr->pNext = p->pNext;
p->pNext = pr;
return ;
}
p = p->pNext;
}
printf("序号过大,请输入大于0,小于等于%d的数值范围 : " , i);
while(n<=0 || n>i)
{
scanf("%d", &n);
}
}
return ;
}
//根据元素内容 插入结点
void Insert_ID(void)
{
char ch[20];
STU *pr;
p = &head;
printf("请输入ID: ");
scanf("%s", ch);
while(p->pNext != NULL)
{
if(strcmp(p->pNext->ID, ch) == 0)
{
pr = (STU *)malloc(sizeof(STU));
printf("请输入插入的工程名 :");
scanf("%s%s%d", pr->ID, pr->name, &pr->score);
pr->pNext = p->pNext;
p->pNext = pr;
return;
}
p = p->pNext;
}
printf("ID不存在\n");
return ;
}
//根据元素进行链表排序。
//选择排序的总体思想是:将已经挑选出来的结点进行删除,再对剩下的结点进行最大值或者最小值筛选。也就意味着,如果原来的链表还有用处,就需要重新创建一个链表,在这个链表上进行排序,再将筛选出的值赋值到第三个链表中。
//对链表进行选择排序法,至少需要两个链表。
void Sort_ID(void)
{
int i=1, j=1;
STU *sort_p, *sort_tail, sort_head, *s_p, s_head, *s_tail;
//sort_为创建的第二个和原链表一模一样的链表
//s_为筛选完的链表。
STU *pp, *pf;
char ch[5];
sort_tail = &sort_head;
s_head.pNext = NULL;
s_tail = &s_head;
//创建第二个链表,在第二个链表上进行处理,原来链表保存。
p = head.pNext;
while(p != NULL)
{
sort_p = (STU *)malloc(sizeof(STU));
sort_p->pNext = NULL;
strcpy(sort_p->name , p->name);
strcpy(sort_p->ID , p->ID);
sort_p->score = p->score;
sort_tail->pNext = sort_p;
sort_tail = sort_p;
p = p->pNext;
}
//下面这段是我用来判断第二个链表有没有赋值成功的,没有实际作用。
/*
sort_p = sort_head.pNext;
if(NULL == sort_head.pNext)
{
printf("表格不存在\n\n");
return ;
}
while(sort_p != NULL)
{
printf("%-3d%-6s%-15s%-4d\n", i, sort_p->ID, sort_p->name, sort_p->score);
sort_p = sort_p->pNext;
i++;
}
printf("\n\n");
*/
//创建第三个链表(最终链表)
s_head.pNext = NULL;
while(1)
{
/*
if(head.pNext == NULL)
{
return;
}
*/
sort_p = &sort_head;
strcpy(ch, sort_p->pNext->ID); //ch存放的是需要进行筛选的最小值,读者若是不会,请自行查阅学习数组中的选择排序法。
pp = NULL; //指针pp用来记住存放最小值ID的“结点地址”。
while(sort_p->pNext->pNext != NULL)
{
if(strcmp(ch, sort_p->pNext->pNext->ID) > 0) //将存放的值和下一项进行比较,若是大于下一项,进行标记。
{
strcpy(ch , sort_p->pNext->pNext->ID); //ch存最小的ID
pp = sort_p->pNext;
}
sort_p = sort_p->pNext;
}
//注意!!链表的选择性排序法,要分清楚三种情况,读者自行理解。提示:首节点、尾结点、中间结点 中的地址域操作方式不同。
if(pp == NULL) //最小值在首节点
{
//转移
s_p = (STU *)malloc(sizeof(STU));
s_p->pNext = NULL;
strcpy(s_p->name, sort_head.pNext->name);
strcpy(s_p->ID, sort_head.pNext->ID);
s_p->score = sort_head.pNext->score;
s_tail->pNext = s_p;
s_tail = s_p;
//删除
pf = sort_head.pNext;
sort_head.pNext = pf->pNext;
free(pf); //重点!!!一定要对无用的malloc地址进行释放。后面如是。
}
else if(pp->pNext->pNext == NULL) //最小值在尾结点。为什么这样写?读者自行体会。提示:需要对尾结点的上一个结点进行操作,所以需要对这个结点进行定位。
{
s_p = (STU *)malloc(sizeof(STU));
s_p->pNext = NULL;
strcpy(s_p->name, pp->pNext->name);
strcpy(s_p->ID, pp->pNext->ID);
s_p->score = pp->pNext->score;
s_tail->pNext = s_p;
s_tail = s_p;
pf = pp->pNext;
pp->pNext = NULL;
free(pf);
}
else //中间结点。
{
s_p = (STU *)malloc(sizeof(STU));
s_p->pNext = NULL;
strcpy(s_p->name, pp->pNext->name);
strcpy(s_p->ID, pp->pNext->ID);
s_p->score = pp->pNext->score;
s_tail->pNext = s_p;
s_tail = s_p;
pf = pp->pNext;
pp->pNext = pf->pNext;
free(pf);
}
if (sort_head.pNext == NULL)
break;
}
//对排完序的第三个链表进行输出:
s_p = s_head.pNext;
if(NULL == s_head.pNext)
{
printf("表格不存在\n\n");
return ;
}
while(s_p != NULL)
{
printf("%-10d%-6s%-15s%-4d\n", i, s_p->ID, s_p->name, s_p->score);
s_p = s_p->pNext;
i++;
}
printf("\n\n");
return ;
}
//对链表中的元素进行修改
void Report(void)
{
char ch[5];
printf("请输入需要单独修改的ID :");
scanf("%s", ch);
p = head.pNext;
while(p != NULL)
{
if(strcmp(p->ID, ch) == 0)
{
printf("请输入需要修改的内容: ");
scanf("%s%d", p->name, &p->score);
}
p = p->pNext;
}
return ;
}
//对创建的链表进行输出文件。主要是为了方便后面的读取文件来创建表格,方便调试,不用在一个一个数据进行输入了。
void Putout(void)
{
FILE *fp;
int i = 1;
fp = fopen("工程.txt", "a");
if(NULL == fp)
{
printf("文件创建失败\n");
return ;
}
p = head.pNext;
while(p != NULL)
{
fprintf(fp, "%-3d%-6s%-15s%-4d\n", i, p->ID, p->name, p->score);
p = p->pNext;
i++;
}
fclose(fp); //若是没有关闭文件,则文件不会创建,因为数据还在缓存区。
printf("创建成功\n\n");
return ;
}
//用文件来读取数据,不用再一个一个输入了,方便调试。
void In_read(void)
{
int n, k, i, m=0;
FILE *fp, *fp1;
if(head.pNext != NULL)
{
printf("表格已存在,按1返回,否则重新覆盖:");
scanf("%d", &n);
if(n == 1)
return ;
Delete_All(); //删除已存在的表格。
}
tail = &head;
fp = fopen("工程.txt", "r");
fp1 = fopen("文本.txt", "w");
if(NULL == fp)
{
printf("工程不存在\n");
return ;
}
while(!feof(fp))
{
p = (STU*)malloc(sizeof(STU));
p->pNext = NULL;
if(k=fscanf(fp, "%d%s%s%d", &i, p->ID, p->name, &p->score) != 4 ) //此处见我另一个博客,有详细的解读。对fscanf/feof的理解。
{
free(p); //正确写法
break;
}
tail->pNext = p;
tail = p;
}
// free(p); 原来是这样写的,这样写,如果文本中刚好文章结束,则把最后一个有效结点给删除了。
p = &head;
fclose(fp);
fclose(fp1);
return ;
}
//删除链表。找到下一个结点,找到之后对当前的结点进行释放。
void Delete_All(void)
{
STU *pr;
p = head.pNext;
pr = p->pNext;
while(pr != NULL)
{
free(p);
p = pr;
pr = p->pNext;
}
free(pr);
head.pNext = NULL;
printf("所有表格删除完成\n");
return ;
}