#include <stdio.h>
#include <stdlib.h> //malloc()函数需要这个头文件,即动态内存申请需要这个头文件
#include <string.h>
//学生信息的定义:
typedef struct Student {
char num[10]; //学号
char name[20]; //姓名
int score; //成绩
} Stu;
//链表结点的定义:
typedef struct Node {
Stu data; //数据域
struct Node *next;//指针域
} Node, *LinkList; //LinkList是Node指针的别名。
//输入学生信息
void scanf_1(Node *head) {
LinkList p, p1; //声明LinkList类型的指针p,p1
//p 在代码中用作指向新创建的链表点的指针。
//p1用于追踪链表的尾结点;它在添加新结点时会使用,以保持链表的结尾。
int i, n; // n表示学生的人数
p1 = head; //p1初始化为头结点,用于最后的结点连接
printf("请输入学生的人数:");
scanf("%d", &n);
printf("请输入学生的信息:\n");
for (i = 1; i <= n; i++) {
p = (LinkList)malloc(sizeof(Node)); //动态内存分配给新结点p
p->next = NULL; //初始化p的下一个结点指针为NULL
printf("学号:");
scanf("%s", p->data.num); //获取学生学号
// 检查学号是否已经存在
LinkList q = head;
int mark = 0;
while (q->next != NULL) {
q = q->next;//将指针 q 指向它的下一个结点(迭代链表)
if (strcmp(q->data.num, p->data.num) == 0) {
mark = 1;//如果找到相同学号,标记为1
printf("这个学号已经存在,请输入一个新的学号.\n");
free(p); // 释放之前分配的内存
i--; // 由于没有添加新学生,所以下一个循环仍应该添加第i个学生
break; // 退出循环
}
}
// 如果学号不存在,则正常继续
if (mark == 0) {
printf("姓名:");
scanf("%s", p->data.name);
printf("成绩:");
scanf("%d", &p->data.score);
// 将新结点附加到链表的末尾
p1->next = p; //将新结点 p 接在 p1 结点之后(将新结点添加到链表末尾)
p1 = p1->next; //将 p1 移动至新的位置,即新插入的结点 p(将p1移动到链表的最后)
}
printf("------------这是一条分界线---------------\n");
}
printf("··················(*^_^*)······················\n");
}
//输出学生信息
void printf_1(Node *head) {
LinkList p;
p = head; //p初始化为头结点,用于最后的结点连接
printf("请输出学生的信息:\n\n");
printf("学号\t姓名\t成绩\n");
//遍历链表并输出信息
/*使用while循环遍历链表。
循环条件是p->next不为NULL,即还没有到达链表的末尾。
在循环中,首先将指针p移动到下一个结点(p->next),
然后输出当前结点的信息,包含三个成员:学号(num)、姓名(name)和成绩(score)。
通过指针解引用,可以访问这些成员的值,并使用printf函数输出。*/
while (p->next != NULL) {
p = p->next;
printf("%s\t%s\t%d\n", p->data.num, p->data.name, p->data.score);
}
}
void search(Node *head, int i) { //这个函数接受两个参数:一个指向Node类型的指针head,表示链表的头结点;一个整数i,表示要查询的位置。
//这里定义了两个变量:一个整数k用于记录遍历到的位置,一个LinkList类型的指针p用于遍历链表。
int k;
LinkList p;//声明一个LinkList类型的指针p
//通过printf函数提示用户输入要查询的位置,然后使用scanf函数读取用户输入的整数并存储在变量i中。
printf("请输入你要查询的位置:");
scanf("%d", &i);
//如果输入的位置i小于0,则打印“查找失败”。这里假设位置是从1开始的,因此负数位置是无效的。
if (i < 0) {
printf("查找失败!\n");
}
//遍历链表并查找位置
/*
使用while循环遍历链表。循环条件有两个:一是p->next不为NULL,即还没有到达链表的末尾;
二是k小于i,即还没有到达要查询的位置。
在循环中,每次迭代将指针p移动到下一个结点,并将k增加1。
*/
p = head;
k = 0;
while (p->next != NULL && k < i) {
p = p->next;
k = k + 1;
}
// 检查是否找到了位置
/*如果输入的位置i等于变量k(表示遍历到的位置),则说明找到了要查询的位置。
输出该位置学生的学号、姓名和成绩。否则,打印“查找失败”。*/
if (i == k)
printf("学号:%s\t 姓名: %s\t成绩: %d\n", p->data.num, p->data.name, p->data.score);
else
printf("查找失败!\n");
}
void delete_1(Node *head, int i, Stu *e) {
/*
head:链表的头结点。
i:要删除的结点的位置。
e:一个指向Stu类型的指针,用于保存被删除结点的数据。
*/
LinkList p, p1; //定义指针p,p1
int k;
p1 = head;
k = 0;
//获取要删除的位置
printf("请输入你要删除的位置:\n");
scanf("%d", &i);
//查找要删除的结点
/*
这个循环用于查找要删除的结点。
循环条件有两个:一是p1->next不为NULL,确保没有到达链表的末尾;
二是k小于i-1,确保没有越界。
在循环中,每次迭代都会将p1移动到下一个结点,并将k增加1。
*/
while (p1->next != NULL && k < i - 1) { // 检查是否找到了要删除的结点
p1 = p1->next;
k++;
}
// 检查是否找到了要删除的结点
if (p1 == NULL) {
printf("删除失败!\n");
}
//删除节点
/*如果找到了要删除的结点,这部分代码将执行。
首先,将p1->next指向的结点的地址赋给p。
然后,将p1->next设置为下一个节点的地址,即跳过了要删除的结点。
接下来,通过指针解引用将被删除结点的数据赋给e。
最后,使用free函数释放被删除结点的内存,并打印“删除成功”。*/
p = p1->next;
p1->next = p->next;
*e = p->data; //同时,还通过指针解引用保存了被删除结点的数据。
free(p);
printf("删除成功!\n");
}
void exit_1() {
printf("退出成功!请按任意键结束!");
exit(0);
}
int main() {
Node *head;//指向链表头结点
head = (LinkList)malloc(sizeof(Node)); //为头结点head分配内存,并且将返回的内存地址赋给head指针。
//LinkList是Node指针的别名。
head->next = NULL; //将头结点的next指针设为NULL,初始化链表为空链表。
int select = -1; //声明并初始化一个名为select的整型变量,用于之后用户选择菜单选项。
int i;
Stu e;//声明一个名为e的Stu结构体变量,用于临时存储和传递学生信息。
while (select != 0) {
printf("*********************\n");
printf("* 1. 添加信息; *\n");
printf("* 2. 显示信息; *\n");
printf("* 3. 位置信息; *\n");
printf("* 4. 删除信息; *\n");
printf("* 5. 退出系统。 *\n");
printf("*********************\n");
printf("\n");
printf("在以上选项选择你要操作的选项的序号:");
scanf("%d", &select);
printf("\n");
switch (select) {
case 1:
scanf_1(head);
break; //调用in函数向链表输入数据
case 2:
printf_1(head);
break;//调用out函数显示所有学生信息
case 3:
search(head, i);
break;//调用search在链表中查找指定位置的学生信息
case 4:
delete_1(head, i, &e);
break;//调用delete_1删除链表中指定位置的学生记录
case 5:
exit_1();
break;//调用exit_1退出程序。
}
}
return 0;
}方法。