一、新建链表
二、链表的插入
链表插入前
链表插入第一步 链表插入第二步
三 、链表的删除
链表删除第一步 链表删除第二步
四、使用双向循环链表完成个人信息的增删改查
/***************************************************************
作用:使用链表完成个人信息的增删改查
作者:殷启翔
版本号:0.1.64
修改时间:2024.7.22
可优化方向: 1.增加查询方式,这里只有通过姓名查询,后续可以添加新的查询项
2.对节点排序
3.修改节点数据时,不用修改整个节点,而修改节点内的某一个项
4.查询方式可以新增用双向链表查寻,正遍历与反遍历,循环查询的遍历结束条件容易出错
***************************************************************/
```c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//使用typedef命令 将 struct geren_xinxi 这个整体重新命名为 geren
//循环链表 中结构体的构成
typedef struct geren_xinxi
{
struct geren_xinxi * before ; //指向上一个结构体
char name[20];
int age;
char sex[10] ;
char where[30] ;
char phone[11] ;
char shijian[20] ;
struct geren_xinxi * next ; //指向下一个结构体
}geren;
```
```c
//申请新的链表节点
geren * jiedian_xin()//返回值为新建的节点的地址
{
geren * p ;
p = (geren *) malloc (sizeof (geren) ) ;
p->before = p ;
p->next = p->before ;
p -> before = p->next ;
return p ;
//新建的节点参考下图
};
```
![新建链表结构](.\新建链表结构.png)
```c
//节点插入
//p_qian 是你想插入那个节点的前一个节点的地址 p_cha 你想插入的节点的首地址
void jiedian_zeng(geren * p_qian , geren * p_cha )
{
p_cha -> next = p_qian -> next ;
p_cha ->before = p_qian ;
p_qian ->next = p_cha ;
p_cha -> next -> before = p_cha ;
//实现的操作请看下图
};
```
![链表插入前](.\链表插入前.png)
![链表插入第一步](.\链表插入第一步.png)
![链表插入第二步](.\链表插入第二步.png)
```c
//节点查
//只能查姓名存不存在 返回值为所搜索到的节点的地址 若没有这个节点返回null
geren * jiedian_cha( geren * start , char search[20] )
{
geren * p = start ;
do
{
//strcmp函数存于<string.h>内,作用比较两个字符串内容是否相等,按位比较,
//前者大于后者返回1,前者小于后者返回-1,相同返回0 ,若其中里面不一样则直接返回,或检测到‘\0’停止
if (strcmp( search , p -> name) == 0 )
{
return p ;
}
p = p ->next ;
}
while( p != start);
printf ("您所寻找的用户不存在\n");
return NULL ;
};
```
![链表组成的理解图](.\链表组成的理解图.png)
```c
//节点改
//输入想修改的节点地址 在函数内部重新赋值输入
//这个结构就是很简单
//利用指针锁定储存内容的结构体,并通过“->”取里面的每一个元素的值
void jiedian_gai( geren * p )
{
printf("请输入姓名\n");
scanf("%s",p -> name);
printf("请输入年龄\n");
scanf("%d", &p -> age);
printf("请输入性别\n");
scanf("%s",p -> sex);
printf("请输入祖籍\n");
scanf("%s",p -> where);
printf("请输入手机号\n");
scanf("%s",p -> phone);
printf("请输入学时间\n");
scanf("%s",p -> shijian);
};
```
```c
//节点删除
void jiedian_shan(geren * p )
{
if (p -> next != p )//这里有一种特殊情况,那就是这个链表中只有它自己一个节点,若如此就是让这个链表全部删除
{
p -> before -> next = p -> next ;
p -> next -> before = p -> before ;
}
free ( p ) ;
//这里比较好的点是p在这个子函数中作为一个形参
//所以p也没有必要让它指向新的节点以保留节点首地址信息
};
```
![链表删除第一步](.\链表删除第一步.png)
![链表删除第二步](.\链表删除第二步.png)
```c
//主函数
int main ()
{
geren * p_last ; // 指向上一层
geren * start ; // 链表起始地址
geren * p_next ; // 指向下一层
geren * p1 ; // 临时用的节点
geren * p2 ; // 临时用的节点
int choice ;
char mingzi[20] ;
market : //后续代码的标志位
printf("请输入您想进行的操作\n");
printf("第一次进入请输入1\n");
printf("添加学生请输入2\n");
printf("删除学生请输入3\n");
printf("查询学生请输入4\n");
printf("修改某名学生的信息请输入5\n");
printf("退出系统请输入0\n");
scanf("%d",&choice);
switch (choice)
{
case 1: //第一次进入
{
//确认首节点,并用start储存这个节点的位置,也是建立链表的第一步
start = jiedian_xin() ;
jiedian_gai( start ) ;
}
goto market;
case 2: //添加学生
//添加学生包括了,新建节点,给节点写入数据,将新建的节点插入链表
//这里为了方便将插入多加了一个查询后加入的功能
{
p1 = jiedian_xin() ;
jiedian_gai( p1 ) ;
printf("您想将这名同学排在哪一名同学后面?\n");
printf("请输入那一名学生的名字\n");
scanf("%s",mingzi);
p2 = (geren*) jiedian_cha( start , mingzi );
jiedian_zeng( p2 , p1 );
}
goto market;
case 3: //删除学生
//删除指定学生,主要还是先查询到那名学生所在的节点,才能删除
{
printf("请输入您想删除那一名学生的名字\n");
scanf("%s",mingzi);
p2 = (geren*)jiedian_cha( start , mingzi );
if (p2 != NULL)
{
jiedian_shan( p2 ) ;
}
}
goto market;
case 4://查询学生
//这里只是查到节点并把那个节点的信息打印出来
{
printf("请输入您想查的那一名学生的名字\n");
scanf("%s",mingzi);
p2 = (geren*)jiedian_cha( start , mingzi );
if (p2 != NULL)
{
printf("姓名:%s\n", p2 -> name);
printf("年龄:%d\n", p2 -> age);
printf("性别:%s\n", p2 -> sex);
printf("祖籍:%s\n", p2 -> where);
printf("手机号:%s\n", p2 -> phone);
printf("入学时间:%s\n", p2 -> shijian);
}
}
goto market;
case 5: //修改某名学生的信息
//先查到学生所在的节点,然后重新给他输入内容即可
{
printf("请输入您想信息的那一名学生的名字\n");
scanf("%s",mingzi);
p2 = (geren*)jiedian_cha( start , mingzi );
if (p2 != NULL)
{
jiedian_gai( p2 );
}
}
goto market;
case 0: //退出系统
{
break ;
}
default:
{
printf("输入错误\n");
goto market ;
}
}
return 0 ;
}
```