链表——增删改查、动态创建(头插法、尾插法)


前言

用c语言实现链表各种的操作,以及需要注意的事项

【链表理论知识】科普链接: https://blog.csdn.net/weixin_43382136/article/details/130531377


一、链表和数组的区别及实现

在这里插入图片描述
数组和链表的区别在于:
数组取的内存空间是连续的,

而链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。
优点是数据的插入或删除都相当方便,有新数据加入就向系统要一块内存空间,数据删除后,就把空间还给系统,不需要移动大量数据。
缺点是设计数据结构时较为麻烦,另外在查找数据时,也无法像静态数据一样可随机读取数据,必须按顺序找到该数据为止。

//数组
#include<stdio.h>
int main()
{
	int i;
	int array[] = {1,3,5,7};
	for(i=0;i<sizeof(array)/sizeof(array[0]);i++)
	{
		printf("%d ",array[i]);
	}
	putchar('\n');
}
//静态链表
#include<stdio.h>
struct Test
{
	int data;
	struct Test *next;
};

int main()
{
	struct Test t1={1,NULL};
	struct Test t2={3,NULL};
	struct Test t3={5,NULL};
	struct Test t4={7,NULL};

	t1.next = &t2;
	t2.next = &t3;
	t3.next = &t4;
	
	printf("%d %d %d", t1.data, t1.next->data, t1.next->next->data, t1.next->next->next->data);
	return 0;
}

二、链表操作

1.链表遍历

#include<stdio.h>

struct Test
{
	int data;
	struct Test *next;
};

//动态遍历
void printLink(struct Test *head)
{
	struct Test *point;
	point = head;
	while(point != NULL){
		printf("%d",point->data); //输出当前指针指向的数据
		point = point->next; //指针向后移
	}
	putchar('\n');
	
}

int main()
{
	//静态创建链表
	struct Test t1={1,NULL};
	struct Test t2={3,NULL};
	struct Test t3={5,NULL};
	struct Test t4={7,NULL};
	t1.next = &t2;
	t2.next = &t3;
	t3.next = &t4;
	
	//静态遍历链
	printf("%d %d %d %d \n", t1.data, t1.next->data, t1.next->next->data,t1.next->next->next->data);
	//动态遍历
	printLink(&t1);//把链表的头指针传进去
	return 0;
}

2.链表节点个数统计

//获取链表节点的个数
int getLinkTotalNum(struct Test *head)
{
	int cnt =0;//定义一个变量来计算链表节点的个数
	while(head != NULL){
		cnt++;
		head = head->next; //指针向后移
	}
	return cnt;
}

3.(改查)链表查找、修改

//链表查找数据
int searchLinkData(struct Test *head,int data)//传入链表头指针和需要查找的数据
{		
		while(head!= NULL)
		{
			if(head->data == data) //查找到数据,返回1
			{
				return 1;
			}
			head = head->next; //指针向后移,与while结合遍历链表
		}
		return 0; //遍历完链表都没有查找到,返回0
}
//链表查找到数据然后【改数据】,head为链表头指针,data为旧数据,newdata为新数据
int gaiLinkData(struct Test *head,int data,int newdata)//传入链表头指针和需要查找的数据
{		
		while(head!= NULL)
		{
			if(head->data == data) //查找到数据并修改数据,返回1表示修改成功
			{
				head->data = newdata;
				return 1;
			}
			head = head->next; //指针向后移,与while结合遍历链表
		}
		return 0; //遍历完链表都没有查找到,返回0
}

4.(增)链表插入(后面插入、前面插入)

4.1 后插法:

在这里插入图片描述

//链表增加节点(后插法),传入链表的头指针head遍历链表,在data的后面插入新节点new
int insertFromBehind(struct Test *head, int data, struct Test *new)
{
	struct Test *p = head;//把链表的头指针赋给p
	
	while(p!= NULL)
	{
		if(p->data == data)//找到数据,在数据后面插入新节点
		{
			new->next = p->next;
			p->next = new;
			return 1; //返回1代表插入成功
		}
		p=p->next; //指针向后移,与while结合遍历链表
	}
	return 0; //遍历完整个链表都没有找到数据,返回0代表插入失败
}

4.2 前插法:

在这里插入图片描述

//链表增加节点【前插法】
//传入链表的头指针head遍历链表,在data的前面插入新节点new,
//并返回新链表的指针
struct Test* insertFromFront(struct Test *head, int data, struct Test *new)
{
	struct Test *p = head;//把链表的头指针赋给p
	
	//如果是链表头的前面插入
	if(p->data == data)
	{
		new->next = p;
		return new;  //返回新链表的头指针
	}
	
	//如果不是链表头的前面插入,判断下一个数据是否为空
	while(p->next!= NULL)
	{
		if(p->next->data == data)//判断下一个数据的值是不是我们要的,是的话就在它前面插入
		{
			new->next = p->next;
			p->next = new;
			return head; //因为链表头没有发生改变,返回链表原本的头指针
		}
		p=p->next; //指针向后移,与while结合遍历链表
	}
	
	printf("插入失败,没有找到这个数据\n");
	return head; //遍历完整个链表都没有找到数据,链表头没有发生改变,返回链表原本的头指针
}

5.(删)链表删除

在这里插入图片描述

//【链表删除节点】
//传入链表的头指针head遍历链表,data为要删除的数据
//并返回新链表的指针
struct Test* deleteData(struct Test *head, int data)
{
	struct Test *p = head;//把链表的头指针赋给p
	
	//如果是删除链表头节点
	if(p->data == data)
	{
		head = head->next;
		//free(p);//释放头节点的内存,这里是静态创建的节点,释放不了,注释掉了
		return head;  //返回新链表的头指针
	}
	
	//如果不是删除链表头节点,判断下一个数据是否为空
	while(p->next!= NULL)
	{
		if(p->next->data == data)//判断下一个数据的值是不是我们要删除的
		{
			//struct Test *tmp = p->next; //这里可能有点争议噢,可能是p,可能是p->next,创建个指针变量释放删除的节点内存空间。静态创建的节点释放不了
			p->next = p->next->next;
			//free(tmp);//释放头节点的内存,这里是静态创建的节点,释放不了,注释掉了
			return head; //因为链表头没有发生改变,返回链表原本的头指针
		}
		p=p->next; //指针向后移,与while结合遍历链表
	}
	
	printf("删除失败,没有找到这个数据\n");
	return head; //遍历完整个链表都没有找到需要删除的数据,返回链表原本的头指针
}

6.完整代码(link.c)

#include<stdio.h>

struct Test
{
	int data;
	struct Test *next;
};

//动态遍历
void printLink(struct Test *head)
{
	struct Test *point;
	point = head;
	while(point != NULL){
		printf("%d ",point->data); //输出当前指针指向的数据
		point = point->next; //指针向后移
	}
	putchar('\n');
	
}

//获取链表节点的个数
int getLinkTotalNum(struct Test *head)
{
	int cnt =0;//定义一个变量来计算链表节点的个数
	while(head != NULL){
		cnt++;
		head = head->next; //指针向后移
	}
	return cnt;
}

//链表查找数据
int searchLinkData(struct Test *head,int data)//传入链表头指针和需要查找的数据
{		
		while(head!= NULL)
		{
			if(head->data == data) //查找到数据,返回1
			{
				return 1;
			}
			head = head->next; //指针向后移,与while结合遍历链表
		}
		return 0; //遍历完链表都没有查找到,返回0
}

//链表查找到数据然后【改数据】,head为链表头指针,data为旧数据,newdata为新数据
int gaiLinkData(struct Test *head,int data,int newdata)//传入链表头指针和需要查找的数据
{		
		while(head!= NULL)
		{
			if(head->data == data) //查找到数据并修改数据,返回1表示修改成功
			{
				head->data = newdata;
				return 1;
			}
			head = head->next; //指针向后移,与while结合遍历链表
		}
		return 0; //遍历完链表都没有查找到,返回0
}

//链表增加节点【后插法】,
//传入链表的头指针head遍历链表,在data的后面插入新节点new
int insertFromBehind(struct Test *head, int data, struct Test *new)
{
	struct Test *p = head;//把链表的头指针赋给p
	
	while(p!= NULL)
	{
		if(p->data == data)//找到数据,在数据后面插入新节点
		{
			new->next = p->next;
			p->next = new;
			return 1; //返回1代表插入成功
		}
		p=p->next; //指针向后移,与while结合遍历链表
	}
	return 0; //遍历完整个链表都没有找到数据,返回0代表插入失败
}

//链表增加节点【前插法】
//传入链表的头指针head遍历链表,在data的前面插入新节点new,
//并返回新链表的指针
struct Test* insertFromFront(struct Test *head, int data, struct Test *new)
{
	struct Test *p = head;//把链表的头指针赋给p
	
	//如果是链表头的前面插入
	if(p->data == data)
	{
		new->next = p;
		return new;  //返回新链表的头指针
	}
	
	//如果不是链表头的前面插入,判断下一个数据是否为空
	while(p->next!= NULL)
	{
		if(p->next->data == data)//判断下一个数据的值是不是我们要的,是的话就在它前面插入
		{
			new->next = p->next;
			p->next = new;
			return head; //因为链表头没有发生改变,返回链表原本的头指针
		}
		p=p->next; //指针向后移,与while结合遍历链表
	}
	
	printf("插入失败,没有找到这个数据\n");
	return head; //遍历完整个链表都没有找到数据,链表头没有发生改变,返回链表原本的头指针
}

//【链表删除节点】
//传入链表的头指针head遍历链表,data为要删除的数据
//并返回新链表的指针
struct Test* deleteData(struct Test *head, int data)
{
	struct Test *p = head;//把链表的头指针赋给p
	
	//如果是删除链表头节点
	if(p->data == data)
	{
		head = head->next;
		//free(p);//释放头节点的内存,这里是静态创建的节点,释放不了,注释掉了
		return head;  //返回新链表的头指针
	}
	
	//如果不是删除链表头节点,判断下一个数据是否为空
	while(p->next!= NULL)
	{
		if(p->next->data == data)//判断下一个数据的值是不是我们要删除的
		{
			//struct Test *tmp = p->next; //这里可能有点争议噢,可能是p,可能是p->next,创建个指针变量释放删除的节点内存空间。静态创建的节点释放不了
			p->next = p->next->next;
			//free(tmp);//释放头节点的内存,这里是静态创建的节点,释放不了,注释掉了
			return head; //因为链表头没有发生改变,返回链表原本的头指针
		}
		p=p->next; //指针向后移,与while结合遍历链表
	}
	
	printf("删除失败,没有找到这个数据\n");
	return head; //遍历完整个链表都没有找到需要删除的数据,返回链表原本的头指针
}

int main()
{
	//静态创建链表
	struct Test t1={1,NULL};
	struct Test t2={3,NULL};
	struct Test t3={5,NULL};
	struct Test t4={7,NULL};
	t1.next = &t2;
	t2.next = &t3;
	t3.next = &t4;
	
	//静态遍历链
	//printf("%d %d %d %d \n", t1.data, t1.next->data, t1.next->next->data,t1.next->next->next->data);
	//动态遍历
	printLink(&t1); //把链表的头指针传进去
	
	//计算链表节点的个数
    //int total =getLinkTotalNum(&t1);
	//printf("链表的节点个数为:%d \n",total);
	
	/*
	//链表查找数据
	int data;
	scanf("%d",&data);//输入需要查找的数据
	int ifData = searchLinkData(&t1,data); //接收返回的结果1或0
	if(ifData == 1)
	{printf("链表存在数据:%d \n",data);}
	else
	{printf("数据不存在");}
	*/
	
	//链表查找到数据然后【改数据】
	int data;
	int newdata;
	
	printf("输入需要修改的旧数据:");
	scanf("%d",&data);//输入需要修改的旧数据
	printf("输入需要修改的新数据:");
	scanf("%d",&newdata);//输入需要修改后的新数据
	int ifData = gaiLinkData(&t1,data,newdata); //接收返回的结果1或0
	if(ifData == 1)
	{
		printf("修改完毕,已经将%d修改成%d \n",data,newdata);
		printLink(&t1);//动态遍历链表
	}
	else
	{printf("修改失败,需要修改的数据不存在");}
	
	
	/*
	//链表增加节点(后插法)
	//创建新节点
	struct Test new={100,NULL};
	//在链表数据3的后面插入新节点
	insertFromBehind(&t1,3,&new); 
	//动态遍历打印链表
	printLink(&t1);
	*/
	
	/*
	//链表增加节点(前插法)
	//创建新节点
	struct Test new2={102,NULL};
	//在链表头数据1的前面插入新节点,并返回新链表的头指针(因为在前面插入,头指针会发生改变)
	struct Test *head = insertFromFront(&t1,1,&new2); 
	//动态遍历打印链表
	printLink(head);
	*/
	
	/*
	//链表删除节点
	//删除数据5,并返回新链表的指针
	struct Test *head2 = deleteData(&t1,5);
	//动态遍历打印链表
	printLink(head2);
	*/
	
	
	return 0;
}

三、链表动态创建

1.头插法

2.尾插法

、注意细节

链表插入时注意细节:

链表的存在主要是**【头指针】**,因为头指针指向链表的头节点,头指针它保存着头节点的地址,又因为链表是靠前面数据的指针一个一个指向后面形成的,所以知道头指针的地址就可以知道整条链表。

在进行链表插入的时候,如果是链表的第一个数据前插入一个新的数据。那么新插入的节点就成为了链表的第一个节点,同时指向第一个节点的头指针就成为了链表的头指针。
那么要记得把:【原先链表的头指针】改为【指向新节点的指针】。
然后把:链表新的头指针地址return返回给主函数中的指针变量。

链表删除时注意细节:

我们一般创建链表都是动态创建链表,所以在删除节点的时候要记得释放那个节点的内存。释放节点的时候free()函数只能释放malloc创建的空间。

未完待续…

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Linux内核使用链表来组织数据。内核链表是通过在[include/linux/list.h]中实现的链表数据结构来实现的。它采用双循环链表机制,每个节点只包含指针域而不包含数据域,这样就可以灵活地扩展数据结构。在内核链表中,list_head结构起着整个链表的衔接作用,它有两个指针域,分别指向下一个节点和上一个节点。初始化链表时,可以使用list_head结构来创建一个空链表。具体的链表操作包括插入节点、删除节点和遍历节点等,这些操作可以在linux内核源码中的list.h文件中找到详细的注释。请注意,链表的源码可能会有一些变化,所以使用时最好参考与你使用的内核版本相对应的源码。如果对链表的使用有任何问题或不正确之处,你可以通过发送邮件到2253238252@qq.com来向我反馈。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [linux内核链表应用](https://blog.csdn.net/qq_18376583/article/details/127353571)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *3* [linux内核链表提取与使用](https://download.csdn.net/download/jiangming7/9370159)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值