Linux系统环境下用C语言实现单链表的基本操作(增删改查)
注:本文中所有代码均在Linux系统环境下编写,若在windows环境下可能出错;本文中部分测试代码为死循环,为的是方便测试,测试完毕可CTRL+C退出程序。
一、动态创建链表
1.1 准备工作
#include<stdio.h>
#include<stdlib.h> //malloc函数必备的头文件
//声明一个结构体作为节点
struct Test
{
int data;
struct Test *next;
};
1.2 头插法动态创建链表
头插法,顾名思义就是在链表的头部之前插入节点,而插入的新节点作为链表的头。
//头插法函数 函数功能:在链表的头部前方插入新节点
struct Test* insertNodeFromHead(struct Test *head,struct Test *new)
{
if(head == NULL){
//若链表头为空,那么新节点作为链表头被返回到creatLinkFromHead函数中
head = new;
return head;
}else{
//若链表头不为空,新节点的下一个节点指向原来的链表头,新节点成为新的链表头被返回
new->next = head;
head = new;
return head;
}
}
//函数功能:通过头插法创建链表
struct Test* creatLinkFromHead(struct Test *head)
{
//定义新节点
struct Test *new = NULL;
while(1){
//为新节点开辟空间
new = (struct Test *)malloc(sizeof(struct Test));
//提示用户输入新节点直到输入为0时退出
printf("You can input a new node until the input is 0: \n");
//获取用户输入
scanf("%d",&(new->data));
//当用户输入节点0退出循环
if(new->data == 0){
printf("stop here\n");
//当用户输入为0时,将为新节点开辟的空间回收
free(new);
return head;
}
//若用户输入不为0,则调用头插法函数获取新链表的头
head = insertNodeFromHead(head,new);
}
}
此时我们头插法动态创建链表已经完成,现在我们需要遍历链表来查看结果。
//函数功能,遍历链表并将结果打印在命令行
void printLink(struct Test *head)
{
//定义指针变量指向链表头
struct Test *p = head;
while(p != NULL){
printf("%d ",p->data);
//从头部遍历链表
p = p->next;
}
//打印完结果命令行换行
putchar('\n');
}
此时我们来测试结果是否正确: main函数中我们输入以下代码
//定义链表头为空,此时链表为空链表
struct Test *head = NULL;
//调用creatLinkFromHead函数创建链表
head = creatLinkFromHead(head);
//打印链表
printLink(head);
我们依次输入 5 4 3 2 1,查看命令行结果
1 2 3 4 5
我们在此基础上再单独调用头插法函数来测试结果
//定义一个新节点
struct Test *new;
//为新节点开辟空间
new = (struct Test *)malloc(sizeof(struct Test));
//为新节点赋值
new->data = 100;
//调用头插法函数插入新节点
head = insertNodeFromHead(head);
//打印链表
printLink(head);
此时查看结果,发现链表头部加入了新节点,结果如下
100 1 2 3 4 5
注明:头插法动态创建链表之所以用两个函数实现头插法与创建链表的功能,是因为无论何时我们都可以单独调用头插法函数,从而实现函数的复用。之后的尾插法动态创建链表也是如此。
1.3 尾插法动态创建链表
尾插法,顾名思义就是在链表的尾部之后插入节点,而插入的新节点作为链表的尾巴也就是最后一位。
//尾插法函数 函数功能:在链表尾巴后面插入新节点
struct Test* insertNodeFromTail(struct Test *head,struct Test *new)
{
//定义指针变量指向链表头
struct Test *p = head;
if(p == NULL){
head = new;
return head;
}
while(p->next != NULL){
//通过指针偏移遍历到链表尾部
p = p->next;
}
//链表尾部节点的下一个节点指向新节点
p->next = new;
return head;
}
//函数功能:利用尾插法创建新链表
struct Test* creatLinkFromTail(struct Test *head)
{
//定义新节点
struct Test *new = NULL;
while(1){
//为新节点开辟空间
new = (struct Test *)malloc(sizeof(struct Test));
//提示用户输入,直到输入为0时退出
printf("You can input a new node until you input 0:\n");
scanf("%d",&(new->data));
//当用户输入为0时,退出循环,返回链表头
if(new->data == 0){
printf("stop here\n");
//用户输入为0,释放为新节点开辟的空间
free(new);
return head;
}
//用户输入不为0时,调用尾插法函数,插入新节点
head = insertNodeFromTail(head,new);
}
}
此时我们尾插法动态创建链表已经完成,测试结果不再演示,感兴趣的小伙伴可以自行测试,测试方法可以参考头插法测试过程
二、链表的基本操作
2.1 计算链表中节点的个数
int nodeNumInLink(struct Test *head)
{
int cnt = 0;
struct Test *p = head;
while(p != NULL){
//cnt作为计数器,每遍历一个节点计数器加一
cnt++;
p = p->next;
}
return cnt;
}
测试代码如下,感兴趣的小伙伴自行测试结果
printf("the number of node inside Link is %d\n",nodeNumInLink(head));
2.2 增删改查之查询节点
int searchNodeInLink(struct Test *head,struct Test *target)
{
struct Test *p = head;
while(p != NULL){
//判断要查找的节点是否存在
if(p->data ==target->data){
return 1;
}
p = p->next;
}
return 0;
}
测试代码如下,感兴趣的小伙伴自行测试。
struct Test *target;
while(1){
//为目标节点开辟空间
target = (struct Test *)malloc(sizeof(struct Test));
//提示用户输入
printf("please input you want search node\n");
//获取用户输入
scanf("%d",&(target->data));
//根据函数返回值判断结果,返回值为1,目标节点存在
if(searchNodeInLink(head,target)){
printf("The node exists\n");
}else{
printf("The node not exists\n");
}
}
2.3 增删改查之增加节点
2.3.1在目标节点前方插入新节点
struct Test* insertBeforeNode(struct Test *head,struct Test *target,struct Test *new)
{
struct Test *p = head;
//判断目标节点是否为头节点
if(p->data == target->data){
new->next = head;
head = new;
return head;
}
while(p->next != NULL){
//判断p的下一个节点是否为目标节点
if(p->next->data == target->data){
//新节点的下一个节点指向目标节点
new->next = p->next;
//p的下一个节点指向新节点
p->next = new;
return head;
}
p = p->next;
}
}
注:之所以用p的下一个节点作为目标节点,是因为如果将p作为目标节点,那么我们无法判断p的前一个节点,从而无法在目标节点之前插入新节点。
测试代码如下,感兴趣的小伙伴自行测试。
struct Test *new;
struct Test *target;
while(1){
//为目标节点开辟空间
target = (struct Test *)malloc(sizeof(struct Test));
//提示用户输入目标节点
printf("input your target node\n");
scanf("%d",&(target->data));
//为新节点开辟空间
new = (struct Test *)malloc(sizeof(struct Test));
//提示用户输入新节点
printf("input you want node insert\n");
scanf("%d",&(new->data));
//调用函数,在目标节点前方插入新节点
head = insertBeforeNode(head,target,new);
printLink(head);
}
2.3.2在目标节点后方插入新节点
struct Test* insertBehindNode(struct Test *head,struct Test *target,struct Test *new)
{
struct Test *p = head;
while(p != NULL){
//判断是否遍历到了目标节点
if(p->data == target->data){
//新节点的下一个指向目标节点的下一个节点
new->next = p->next;
//目标节点的下一个指向新节点
p->next = new;
return head;
}
p = p->next;
}
}
测试代码如下,感兴趣的小伙伴自行测试。
struct Test *new;
struct Test *target;
while(1){
//为目标节点开辟空间
target = (struct Test *)malloc(sizeof(struct Test));
//提示用户输入目标节点
printf("input your target node\n");
//获取用户输入
scanf("%d",&(target->data));
//为新节点开辟空间
new = (struct Test *)malloc(sizeof(struct Test));
//提示用户输入新节点
printf("input you want node insert\n");
//获取用户输入
scanf("%d",&(new->data));
//调用函数插入新节点
head = insertBehindNode(head,target,new);
//打印结果
printLink(head);
}
2.3 增删改查之删除节点
struct Test* deleteNode(struct Test *head,struct Test *target)
{
struct Test *p = head;
//判断目标节点是否为头节点
if(p->data == target->data){
//原来头节点的下一个节点作新的头节点
head = head->next;
return head;
}
while(p->next != NULL){
//判断p的下一个节点是否为目标节点
if(p->next->data == target->data){
//p的下一个节点指向p->next的下一个节点
p->next = p->next->next;
return head;
}
p = p->next;
}
}
注:之所以将p的下一个节点作为目标节点,是因为如果用p来作目标节点,当我们删除目标节点时,无法判断p的前一个节点,最终无法删除。
测试代码如下,感兴趣的小伙伴自行测试。
struct Test *target;
while(1){
//为目标节点开辟空间
target = (struct Test *)malloc(sizeof(struct Test));
//提示用户输入
printf("input your target node\n");
//获取用户输入
scanf("%d",&(target->data));
//调用函数删除目标节点
head = deleteNode(head,target);
//打印结果
printLink(head);
}
2.3 增删改查之修改节点
struct Test* modifyNode(struct Test *head,struct Test *target,struct Test *new)
{
struct Test *p = head;
while(p !=NULL){
//判断是否为目标节点
if(p->data == target->data){
//将目标节点的整型变量修改为想要的数据
p->data = new->data;
return head;
}
p = p->next;
}
}
测试代码如下,感兴趣的小伙伴自行测试。
struct Test *new;
struct Test *target;
while(1){
//为新节点开辟空间
target = (struct Test *)malloc(sizeof(struct Test));
//提示用户输入
printf("input your target node that you will modify\n");
//获取用户输入
scanf("%d",&(target->data));
//为新节点开辟空间
new = (struct Test *)malloc(sizeof(struct Test));
//提示用户输入
printf("input you new node that you want \n");
//获取用户输入
scanf("%d",&(new->data));
//调用函数修改目标节点
head = modifyNode(head,target,new);
//打印结果
printLink(head);
}
希望本文可以对大家有所帮助,本人才疏学浅,如有错误之处,欢迎指正!
本文师承上官可编程!