在我们学习C语言基础的过程中,指针和链表一直是新手的两大拦路虎。在大学的程序语言课程设计中,我们在设计系统时可能会用到链表或者结构体数组。今天,我想为大家梳理一下关于单链表的使用。本文章需要读者先行理解C语言的结构体和结构体中next的使用。
注:我所使用的是vs2019,部分代码可能在其他编译器中会报错。
在我理解中,单链表分为两类,一种是有表头单链表,一种是无表头单链表。本章我先来帮助大家理解有表头单链表。
结构体框架
struct Structure //为了方便大家理解char类型和int类型在使用上的区别,我特地分别设置了两个变量
{
char num[4];//编号
int score;//分数
char name[10];//姓名
int room;//房间号
struct Structure* next;
};
有表头单链表,顾名思义,就是创建的链表中表头数据为NULL,next直接指向下一结构体。它包含的基础功能有:添加信息,查找信息,删除信息,信息排序,文件操作,信息显示。文件操作功能我暂且放在下一章讲解。
下面,我来讲解使用有表头单链表需要使用到的函数:
struct Structure* create()//创建有表头链表(不存数据)
{
struct Structure* head = (struct Structure*)malloc(sizeof(struct Structure));//产生变量
head->next = NULL;//初始化变量
return head;
}
我们需要在主函数main中编写 struct Structure* head = create(); 这样一个链表的表头就形成了。
struct Structure* newNote()//创建节点
{
struct Structure* p = (struct Structure*)malloc(sizeof(struct Structure));
scanf_s("%s%d%s%d", p->num, 4, &p->score, p->name, 10, &p->room);
p->next = NULL;
return p;
}
scanf_s代码解释: // scanf_s("%s%d%s%d", p->num, 4, &p->score, p->name, 10, &p->room);
1.代码解读:num和name均为char类型的数组变量,在取址时可以不用添加“&”,后面跟着的4,10为可读取数据的长度。score和room为int类型变量,需添加“&”读取地址
2.这是vs新版本中为了保证系统的安全,添加了检验机制。
3.它与scanf的区别在于,读取字符串时可以确定读取的长度,当输入字符串长度过长时可以直接对该变量赋值为空,且不影响后续数据的读取。若输入数据为123456 78 张三 101,则存储的数据为:num="" score=78 name="张三" room=101 。
函数解读,创建一个结构体,并存储用户输入数据,并将该结构体返回。
void insert(struct Structure* head)//调用创建节点导入数据并从表头插入
{
int n;//用于读取用户输入的记录个数
printf("请输入你要录入的学生个数:");
scanf_s("%d", &n);
for (; n < 1; n--)
{
printf("请输入数据\n");//也可以定义变量i来提示用户当前输入的记录条数
struct Structure* node = newNote();
node->next = head->next;
head->next = node;
}
}
将用户输入的记录一个个从表头插入链表中。
Structure* Query(Structure* head, char* num)//查找编号,有则返回对应结构体,否则返回空链表
{
Structure* p;
p = head;
while (p != NULL)
{
if (strcmp(num, p->num) == 0)
return p;
p = p->next;
}
return NULL;
}
本函数中使用了strcmp()函数,需使用头文件#include
本函数从表头开始查找,编号相同则返回,直到查找到链表尾步为止,若无对应编号则返回空值。
常被query a record查找函数和delete删除函数调用。
void Query_a_record(Structure* head)//接收查找反馈并展示结果
{
char s[4];
Structure* p;
printf("请输入一个要查找的编号\t");
scanf_s("%s", s, 4);
getchar();
p = Query(head, s);
if (p != NULL)
{
printf("%s %d %s %d", p->num, p->score, p->name, p->room);
system("pause");
}
else
{
printf("找不到对应的编号!\t\t");
system("pause");
}
}
调用query查找函数,分两种情况,若查找到记录则显示;若返回值为空则链表没有该编号。
缺点:无法判断链表是否存储数据
Telephone* Delete(Telephone* head, char* num)//接收数据,并删除编号相同的记录
{
Telephone* p1, * p2=NULL;
p1 = head;
while ((strcmp(num, p1->num) != 0) && (p1->next != NULL))
{
p2 = p1; p1 = p1->next;
}
if (strcmp(num, p1->num) == 0)
{
if (p1 == head) head = p1->next;
else p2->next = p1->next;
free(p1);
}
return head;
}
用于接收来自delete a record删除函数传递的编号,然后删除第一个与之编号相同的记录
缺点:只能删除一个
七、delete a record()用于删除信息功能,删除查询的记录
Structure* Delete_a_record(Structure* head)//传递要删除的记录编号,并选择是否删除
{
char s[4];
printf("请输入你要删除的编号:\t");
scanf_s("%s", s, 4);
getchar();
if (Query(head, s) != NULL)
{
head = Delete(head, s);
printf("编号为%s的记录已删除", s);
system("pause");
}
else
{
printf("编号为%s的记录未查询到", s);
system("pause");
}
return head;
}
通过调用query查找函数来确定编号是否存在,然后调用delete删除函数来删除记录。
void sort(struct Structure* head)//排序
{
int i, j, n = 0;
Structure t, * p = head, * p1, * p2;
while (p != NULL)//统计结构体数量
{
n++;
p = p->next;
}
p2 = head;
for (i = 0; i < n - 1; i++)
{
p1 = p2->next;
p = p2;
for (j = i + 1; j < n; j++)
{
if (strcmp(p1->num, p->num) < 0)
p = p1;
p1 = p1->next;
}
if (p != p2)
{
strcpy(t.num, p2->num);
strcpy(t.name, p2->name);
t.room = p2->room;
t.score = p2->score;
strcpy(p2->num, p->num);
strcpy(p2->name, p->name);
p2->room = p->room;
p2->score = p->score;
strcpy(p->num, t.num);
strcpy(p->name, t.name);
p->room = t.room;
p->score = t.score;
}
p2 = p2->next;
}
printf("升序排列操作已完成!\t");
system("pause");
}
t是结构体变量,是交换的载体。而p1,p2是结构体指针。所以在调用结构体成员时,t使用".",p1使用"->"。
void Display(Structure* head)//显示记录
{
Structure* p = head;
printf("编号\t\t成绩\t\t姓名\t\t教室\n");
p = p->next;//跳过表头
while (p != NULL)
{
printf("%s\t\t%d\t\t%s\t\t%d\n", p->num, p->score, p->name, p->room);
p = p->next;
}
system("pause");
}
#define _CRT_SECURE_NO_WARNINGS
#include
#include
#include
//为了方便大家理解char类型和int类型在使用上的区别,我特地分别设置了两个变量
struct Structure
{
char num[4];//编号
int score;//分数
char name[10];//姓名
int room;//房间号
struct Structure* next;
};
//创建有表头链表(不存数据)
struct Structure* create()
{
struct Structure* head = (struct Structure*)malloc(sizeof(struct Structure));//产生变量
head->next = NULL;//初始化变量
return head;
}
//创建节点
struct Structure* newNote()
{
struct Structure* p = (struct Structure*)malloc(sizeof(struct Structure));
scanf_s("%s%d%s%d", p->num, 4, &p->score, p->name, 10, &p->room);
p->next = NULL;
return p;
}
//调用创建节点导入数据并从表头插入
void insert(struct Structure* head)
{
int n;//用于读取用户输入的记录个数
printf("请输入你要录入的学生个数:");
scanf_s("%d", &n);
for (; n > 0; n--)
{
printf("请输入数据\n");//也可以定义变量i来提示用户当前输入的记录条数
struct Structure* node = newNote();
node->next = head->next;
head->next = node;
}
}
//查找编号,有则返回对应结构体,否则返回空链表
Structure* Query(Structure* head, char* num)
{
Structure* p;
p = head;
while (p != NULL)
{
if (strcmp(num, p->num) == 0)
return p;
p = p->next;
}
return NULL;
}
//接收查找反馈并展示结果
void Query_a_record(Structure* head)//接收查找反馈并展示结果
{
char s[4];
Structure* p;
printf("请输入一个要查找的编号\t");
scanf_s("%s", s, 4);
getchar();
p = Query(head, s);
if (p != NULL)
{
printf("%s %d %s %d", p->num, p->score, p->name, p->room);
system("pause");
}
else
{
printf("找不到对应的编号!\t\t");
system("pause");
}
}
//接收数据,并删除编号相同的记录
Structure* Delete(Structure* head, char* num)
{
Structure* p1, * p2 = NULL;
p1 = head;
while ((strcmp(num, p1->num) != 0) && (p1->next != NULL))
{
p2 = p1; p1 = p1->next;
}
if (strcmp(num, p1->num) == 0)
{
if (p1 == head) head = p1->next;
else p2->next = p1->next;
free(p1);
}
return head;
}
//传递要删除的记录编号,并选择是否删除
Structure* Delete_a_record(Structure* head)
{
char s[4];
printf("请输入你要删除的编号:\t");
scanf_s("%s", s, 4);
getchar();
if (Query(head, s) != NULL)
{
head = Delete(head, s);
printf("编号为%s的记录已删除", s);
system("pause");
}
else
{
printf("编号为%s的记录未查询到", s);
system("pause");
}
return head;
}
//排序
void sort(struct Structure* head)
{
int i, j, n = 0;
Structure t, * p = head, * p1, * p2;
while (p != NULL)//统计结构体数量
{
n++;
p = p->next;
}
p2 = head;
for (i = 0; i < n - 1; i++)
{
p1 = p2->next;
p = p2;
for (j = i + 1; j < n; j++)
{
if (strcmp(p1->num, p->num) < 0)
p = p1;
p1 = p1->next;
}
if (p != p2)
{
strcpy(t.num, p2->num);
strcpy(t.name, p2->name);
t.room = p2->room;
t.score = p2->score;
strcpy(p2->num, p->num);
strcpy(p2->name, p->name);
p2->room = p->room;
p2->score = p->score;
strcpy(p->num, t.num);
strcpy(p->name, t.name);
p->room = t.room;
p->score = t.score;
}
p2 = p2->next;
}
printf("升序排列操作已完成!\t");
system("pause");
}
//显示记录
void Display(Structure* head)
{
Structure* p = head;
printf("编号\t\t成绩\t\t姓名\t\t教室\n");
p = p->next;//跳过表头
while (p != NULL)
{
printf("%s\t\t%d\t\t%s\t\t%d\n", p->num, p->score, p->name, p->room);
p = p->next;
}
system("pause");
}
void Display_Main_Menu()
{
printf("1.添加信息\n");
printf("2.删除信息\n");
printf("3.查找信息\n");
printf("4.排序信息\n");
printf("5.显示信息\n");
printf("0.退出系统\n");
}
int main()
{
int choose, b;
struct Structure* head = create();
while (1)
{
Display_Main_Menu();
scanf_s("%d", &choose);
switch (choose)
{
case 1:
insert(head);
system("cls");
break;
case 2:
Delete_a_record(head);
system("cls");
break;
case 3:
Query_a_record(head);
system("cls");
break;
case 4:
sort(head);
system("cls");
break;
case 5:
Display(head);
system("cls");
break;
case 0:
goto loop;
default:
printf("\t请输入选项0—9!\n");
system("pause");
}
}
loop:
printf("感谢本次使用成绩处理系统\n");
system("pause");
}