单链表——增删改查(超详细)
文章目录
- 主函数
- 增
- 删
- 改
- 查
- 销毁单链表
- 运行结果展示
【单链表——增删改查(超详细)】链表是一种常见树数据结构。链表就像是一个班级的学生信息表,信息表中存储有学生的学号,姓名等个人信息,信息表中每一行存储一个学生的信息,而链表中每个节点存储一个学生的信息,但是学生信息表中的个人信息是以表格的形式按学号依次排列的,其物理位置是有顺序的,而链表在计算机内存中链表是无序的(链表只是逻辑位置上是有序的,而在物理位置上是无序的),计算机想要访问内存中的数据就得先要知道存储该数据的地址,所以计算机想要知道链表中学生的信息,就要先找到存储该学生信息节点的地址,因此链表设置了一个头节点,该节点存储着第一个节点的地址,其余节点中存储着学生的信息(信息域)和下一节点的地址(指针域),即第一个节点存储着第一个学生的个人信息和第二个节点的地址,第二个节点存储着第二个学生的个人信息和第三个节点的地址,以此类推。所以计算机想要知道链表中某一个学生的信息就要从第一个节点开始依次查询,直到找出存储那一个学生信息的节点为止。如果链表中没有存储你查询的学生的信息,那么计算机是不是会一直访问链表呢?链表中最后一个节点的指针域是指向NULL的,NULL作为访问链表的结束标志,当计算机访问到NULL时结束该次访问。
特点:当访问一个节点后,只能接着访问下一个节点,而无法返回访问上一个节点(既然选择了远方,便只顾风雨兼程)
优点:存储的元素个数不受限制(内存充足即可),可以随意的增加和减少元素个数
缺点:不可以随机的访问链表节点
编译环境:vs2017
主函数
#include
#include
#include
#pragma warning(disable:4996)typedef struct Users
{
long id;
//账号
char name[10];
//用户名
long long num;
//联系电话
struct Users *next;
}Users;
void AddUsers(Users **);
void DeleteUsers(Users **);
void ModifyUsers(Users *);
void FindUsers1(Users *);
void FindUsers2(Users *);
void FreeList(Users **);
int main()
{
Users *head = NULL;
// 定义一个头指针
int n;
while (1)
{
printf("\t\t\t\t1.增加用户\n");
printf("\t\t\t\t2.删除用户\n");
printf("\t\t\t\t3.修改用户信息\n");
printf("\t\t\t\t4.查询用户信息\n");
printf("\t\t\t\t5.查询所有用户信息\n");
printf("\t\t\t\t6.退出\n");
printf("\t\t\t请选择:");
scanf_s("%d",&n);
switch (n)
{
case 1:AddUsers(&head);
system("pause");
system("cls");
break;
case 2:DeleteUsers(&head);
system("pause");
system("cls");
break;
case 3:ModifyUsers(head);
system("pause");
system("cls");
break;
case 4:FindUsers1(head);
system("pause");
system("cls");
break;
case 5:FindUsers2(head);
system("pause");
system("cls");
break;
default:
FreeList(&head);
return 0;
}
}
}
增
//尾插法
void AddUsers(Users **p)//注意是 **p 而不是 *p,为什么用 **p 而不是 *p 自己思考一下哈,有助于自己的理解
{
system("cls");
Users *p1 = *p, *p2 = NULL;
while (p1 != NULL)//寻找最后一个节点
{//大家可以停下,思考一下 p2 在后面有什么作用哦
p2 = p1;
p1 = p1->next;
} Users *q1 = NULL, *q2 = *p;
q1 = (Users *)malloc(sizeof(Users));
printf("请输入账号\n");
scanf_s("%ld", &q1->id);
if (*p != NULL)
{
while (1)//检查账号是否重复
{
if (q1->id == q2->id)
{
printf("此用户名已存在!请重新注册!\n");
free(q1);
//要记得释放动态内存噢,要不然后果很严重哈(自己体会)
return;
}
else
{
q2 = q2->next;
if (q2 == NULL)
break;
}
}
}
printf("请输入用户名:");
scanf_s("%s", q1->name,10);
printf("请输入联系电话:");
scanf("%lld", &q1->num);
if (*p == NULL)//大家想一下为什么要判断头节点指向 NULL 哈。问题虽然是小问题但是还是得多多注意哈
{
*p = q1;
q1->next = NULL;
//尾节点一定要指向 NULL 哈。这点很重要哟,为什么这很重要呢?
}
else
{
p2->next = q1;
q1->next = NULL;
}
printf("账户注册成功!\n");
}
删
void DeleteUsers(Users **p)
{
system("cls");
Users *p1 = *p, *p2 = NULL;
long id;
int n = 0;
printf("请输入你要删除用户的账号:");
scanf_s("%ld", &id);
while (p1 != NULL && p1->id != id)//()里面的判断顺序不能改变,要不然当 p1 指向空的时候程序会出错
{
n++;
p2 = p1;
//p2 有什么作用?(提示:回忆链表是如何删除节点的)
p1 = p1->next;
}
if (p1 == NULL)//链表为空或者链表中无该账号
{
printf("无该用户信息\n");
return;
}
else if (n == 0)//注意这个条件与上面一个条件的顺序哈,当两个条件调换时是否会改变程序的功能。自己实践一下哈,所谓实践出真知
{
*p = p1->next;
free(p1);
//一定要记得释放动态内存
printf("删除成功\n");
}
else
{
p2->next = p1->next;
free(p1);
printf("删除成功\n");
}
}
改
void ModifyUsers(Users *p)
{
system("cls");
long id, n;
long long num;
char a[10];
printf("请输入你需要查找的帐号:");
scanf_s("%ld", &id);
while (p != NULL && p->id != id)
{
p = p->next;
}
if (p == NULL)//修改、查找用户信息的第一前提是判断有无该用户
{
printf("无该用户信息\n");
return;
}
while (1)
{
printf("\t\t\t\t\t1.修改用户名\n");
printf("\t\t\t\t\t2.修改联系电话\n");
printf("\t\t\t\t\t3.返回上一级\n");
printf("\t\t\t\t请选择:");
scanf("%ld", &n);
switch (n)
{
case 1:
printf("请输入用户名:");
scanf("%s", a);
strcpy(p->name, a);
printf("修改成功\n");
break;
case 2:
printf("请输入联系电话:");
scanf_s("%lld", &num);
p->num = num;
printf("修改成功\n");
break;
default:return;
}
}
}
查
void FindUsers1(Users *p) //查询用户信息
{
system("cls");
long id;
printf("请输入你需要查询的账号:");
//这里是用账号查找,用用户名或者其它查找方法还是一样的
scanf_s("%ld", &id);
while (p != NULL && p->id!=id)
{
p = p->next;
}
if (p == NULL)
{
printf("无该用户信息\n");
return;
}
printf("账号:%ld\t用户名:%s\t联系电话:%lld\n", p->id, p->name, p->num);
}void FindUsers2(Users *p) //查询所有用户信息
{
system("cls");
if (p == NULL)//注意第一前提哈
{
printf("无用户信息\n");
return;
}
while (p != NULL)
{
printf("账号%ld\t用户名:%s\t联系电话:%lld\n", p->id, p->name, p->num);
p = p->next;
}
}
销毁单链表
void FreeList(Users **p)
{
Users *temp , *q = *p;
while (q != NULL)
{
temp = q;
q = q->next;
//temp指向下一个的地址
free(temp);
//释放当前节点
}
}
运行结果展示
文章图片
文章图片
文章图片
文章图片
文章图片
这篇文章花了挺长时间的,做了很多细节的修改,但由于时间、水平、精力有限,文中难免会出现不准确的、甚至错误的地方,欢迎大佬看见的话批评指正。
推荐阅读
- 急于表达——往往欲速则不达
- 慢慢的美丽
- 《真与假的困惑》???|《真与假的困惑》??? ——致良知是一种伟大的力量
- 增长黑客的海盗法则
- 2019-02-13——今天谈梦想()
- 考研英语阅读终极解决方案——阅读理解如何巧拿高分
- Ⅴ爱阅读,亲子互动——打卡第178天
- 低头思故乡——只是因为睡不着
- 取名——兰
- 每日一话(49)——一位清华教授在朋友圈给大学生的9条建议