C语言基础 — ( C语言的链表实例)

欢迎小伙伴的点评✨✨ 本篇章系列是对C语言的深度思考和总结、关于C语言内容会持续更新

文章目录

  • 前言
  • 一、什么是链表
  • 二、建立简单静态链表
  • 二、建立简单动态链表
  • 三、链表的增加、删除、更改、查询
  • 四、总结


前言

本章会给大家带来基于C语言链表的实例。

一、什么是链表

链表是一种常见的重要的数据结构。链表是动态地进行存储分配的一种结构,它会根据所需要开辟内存单元。
链表有一个”头指针“变量,它存放一个地址,该地址指向一个元素,链表中每一个元素称为“结点”,每个结点都应该包括两个部分:
1、用户需要用的实际数据;
2、下一个结点的地址。可以看出,“头指针” 指向地一个元素,第1个元素又指向第2个元素 … 直到最后一个元素,该元素不再指向其他元素,它称为"表尾“,它的地址部分放一个”NULL“ (表示”空地址“),链表到此结束。
这样的一种链表数据结构,其元素在内存中的地址可以是不连续的。要想找到某一元素,必须先找该元素的上一个元素,根据它提供的下一元素地址才能找到下一个元素。如果不提供 ”头指针“,则整个链表都无法访问。链表如同一条铁链一样,一环扣一环,中间是不能断开的。显然,链表这种数据结构,必须利用指针变量才能实现,即一个结点中应包含一个指针变量,用它存放下一结点的地址。

二、建立简单静态链表

例如,可以设计这样一个结构体类型:

struct   data
{
	int num;
	struct data *next;   //next 是指针变量,指向结构体变量
};

注意:上面只是定义一个struct data 类型,并未实际分配存储空间,只有定义了变量才分配存储单元。
其中,成员num用来存放结点中的数据(用户需要用到的数据),next 是指针类型的成员,它指向struct data 类型数据(就是next 所在的结构体类型)。一个指针类型的成员既可以指向其他类型的结构体数据,也可以指向自己所在的结构体类型数据。现在,next 是struct data 类型中的一个成员,它又指向struct data 类型的数据。用这种方法就可以建立链表。
单结点代码实例如下:

#include <stdio.h>
#pragma pack(1)  //字节对齐
struct   data
{
	int num;
	struct data* next;   //next 是指针变量,指向结构体变量
};

int main()
{
	struct data  a, b, c, * head, * p; //定义3个结构体变量a,b,c作为链表的结点;
	a.num = 0;  //对结点a的num成员赋值;
	b.num = 1;  //对结点b的num成员赋值; 
	c.num = 2;  //对结点c的num成员赋值;
	head = &a;  //将结点a的起始地址赋值给头指针head;
	a.next = &b; //将结点b的起始地址赋给a结点的next成员;
	b.next = &c; //将结点c的起始地址赋给b结点的next成员;
	c.next = NULL; //c结点的next成员不存放其他结点地址;
	p = head;     //使p指向a结点;
	while (p != NULL)  //输出完c结点后的值为NULL,循环终止;
	{
		printf("%d\n", p->num);  //输出p指向的结点的数据;
		p = p->next;             //使p指向下一结点;

	}
	return 0;
}

编译运行结果如下:
在这里插入图片描述
多结点代码实例如下:

#include <stdio.h>
#pragma pack(1)  //字节对齐
struct   data
{
	int num;
	struct data* next_1;   //next_1 是指针变量,指向结构体变量
	struct data* next_2;   //next_2 是指针变量,指向结构体变量
};

int main()
{
	struct data  a, b, c,d,e,f, * head_1,*head_2, * p; //定义3个结构体变量a,b,c作为链表的结点;
	a.num = 0;  //对结点a的num成员赋值;
	b.num = 1;  //对结点b的num成员赋值; 
	c.num = 2;  //对结点c的num成员赋值;
	d.num = 3;  //对结点a的num成员赋值;
	e.num = 4;  //对结点b的num成员赋值; 
	f.num = 5;  //对结点c的num成员赋值;
	head_1 = &a;  //将结点a的起始地址赋值给头指针head_1;
	a.next_1 = &b; //将结点b的起始地址赋给a结点的next_1成员;
	b.next_1 = &c; //将结点c的起始地址赋给b结点的next_1成员;
	c.next_1 = NULL; //c结点的next_1成员不存放其他结点地址;

	head_2 = &d;   //将结点d的起始地址赋值给头指针head_2;
	d.next_2 = &e; //将结点e的起始地址赋给d结点的next_2成员;
	e.next_2 = &f; //将结点f的起始地址赋给e结点的next_2成员;
	f.next_2 = NULL; //f结点的next_2成员不存放其他结点地址

	p = head_1;     //使p指向a结点;
	while (p != NULL)  //输出完c结点后的值为NULL,循环终止;
	{
		printf("%d\n", p->num);  //输出p指向的结点的数据;
		p = p->next_1;             //使p指向下一结点;

	}
	p = head_2;     //使p指向a结点;
	while (p != NULL)  //输出完c结点后的值为NULL,循环终止;
	{
		printf("%d\n", p->num);  //输出p指向的结点的数据;
		p = p->next_2;             //使p指向下一结点;

	}
	return 0;
}

编译运行结果如下:
在这里插入图片描述

二、建立简单动态链表

所谓建立动态链表是指在程序执行过程中从无到有地建立起一个链表,即一个一个地开辟结点和输入各结点数据,并建立起前后相链的关系。使用动态内存分配的形式,对所需的数据分配空间。
代码实例如下:

#include <stdio.h>
#include <stdlib.h>
#pragma pack(1)
struct   data
{
	int num;
	struct data* next;   //next是指针变量,指向结构体变量
};

int main()
{
	struct data   * head ,* p1,*p2; //定义3个结构体指针变量head,p1,p2用来指向struct data类型数据的;
	head = p1 = p2 = (struct data*)malloc(sizeof(struct data)); //开辟内存动态存储区,把起始地址赋值给 head;
	
	for (int i = 0; i < 3; i++)
	{
		p1 -> num = i;        //给指针p1指向的结构体成员 num 赋值;
		p2 -> next = p1;      //把指针p1的地址赋值给 指针p2指向的结构体成员指针next;
		p2 = p1;              //把指针p1的地址赋值给 p2
							 /*注意此时指针p1的地址分别赋值给了指针p2和结构体成员指针next*/
		p1 = (struct data*)malloc(sizeof(struct data));  //重新给p1开辟新空间;
	}  //此时进入下一个循环,指针p1、指针p2、当前结构体指针next,向后移动一个sizeof(struct data)单元;
	   //由于未释放内存动态存储区,故结构体与结构体之间使用指针 next 连接了起来。
		//在这个过程中,指针p1 和 指针 p2 的作用就是交换地址,使结构体成员 next 连接到下一个结构体; 
	p2->next = NULL; //当循环结束时,给指针p2指向的结构体成员指针next赋值为0(NULL);
	while (head != NULL)
	{
		
			printf("%d\n", head->num);  //输出p指向的结点的数据;
			head = head->next;   //使p指向下一结点;
	} 		       
	return 0;
}

编译后如下所示:
在这里插入图片描述

三、链表的增加、删除、更改、查询

1、链表查询:循环遍历所需要的结点信息,或者根据已知第几个结点进行搜索、查询结构体数据即可
2、链表插入:循环遍历所需要的结点信息,或者根据已知第几个结点,当找到所需要的结点时,插入结构体即可
3、链表删除:循环遍历所需要的结点信息,或者根据已知第几个结点,当找到所需要的结点时,删除结构体即可
4、链表更改:循环遍历所需要的结点信息,或者根据已知第几个结点,当找到所需要的结点时,更改结构体数据即可
代码实例如下:

#include <stdio.h>
#include <stdlib.h>

int init(); //初始化结点函数;
int head_add(int da);  //在头的前面增加结点函数;
int body_add(int num, int da);  //在中间增加结点函数;
int tail_add(int da);  //在尾的后面增加结点函数;

int head_dele();  //在头的前面删除结点函数;
int body_dele(int num);  //在中间删除结点函数;
int tail_dele();  //在尾的后面删除结点函数;

int head_change(int da);  //更改头结点函数;
int body_change(int num, int da);  //更改中间结点函数;
int tail_change(int da);  //更改尾结点函数;
void examine(int num);//查结点
void Inquire();//遍历输出
#pragma pack(1)
struct   data
{
	int num;
	struct data* next;   //next是指针变量,指向结构体变量
};

struct data* head, *tail,* p1 ,* p2,*ptemp,*pt2; //定义3个结构体指针变量head,p1,p2用来指向struct data类型数据的;

int main()
{
	if (init() == 0)
	{
		printf("*******************初始化成功!**********************\n");
	}
	head_add(12);  //增加头
	body_add(2,88);//增加中
	tail_add(13);//增加尾
	head_dele(); //删除头
	body_dele(1);//删除中
	tail_dele();//删除尾
	head_change(9);//更改头
	body_change(1,8);//更改中
	tail_change(7); //更改尾
	examine(1); //查询
	return 0;
}

/*************************初始化*****************************/
int init()
{
	head = p1 = p2 = (struct data*)malloc(sizeof(struct data)); //开辟内存动态存储区,把起始地址赋值给 head;
	for (int i = 0; i < 3; i++)
	{
		p1->num = i;        //给指针p1指向的结构体成员 num 赋值;
		p2->next = p1;      //把指针p1的地址赋值给 指针p2指向的结构体成员指针next;
		p2 = p1;              //把指针p1的地址赋值给 p2
		/*注意此时指针p1的地址分别赋值给了指针p2和结构体成员指针next*/
		p1 = (struct data*)malloc(sizeof(struct data));  //重新给p1开辟新空间;
	}  //此时进入下一个循环,指针p1、指针p2、当前结构体指针next,向后移动一个sizeof(struct data)单元;
	   //由于未释放内存动态存储区,故结构体与结构体之间使用指针 next 连接了起来。
		//在这个过程中,指针p1 和 指针 p2 的作用就是交换地址,使结构体成员 next 连接到下一个结构体; 
	p2->next = NULL; //当循环结束时,给指针p2指向的结构体成员指针next赋值为0(NULL);
	tail = p2; //保存到尾结点,用于尾结点的增加和删除;
	Inquire();//遍历输出
	return 0;
}

int head_add(int da) //在头的前面增加
{	
	p1 = (struct data*)malloc(sizeof(struct data));  //重新给p1开辟新空间;
	p1->num = da;     //给新的结点设置数据;
	p1->next = head;  //结构体指针p1的成员指针next指向头指针head;
	head = p1;        //将结构体指针p1内存地址赋值给头指针head;
	printf("********************head_add执行成功!*************************\n");
	Inquire();//遍历输出
	return 0;
}


/*******************
int body_add(int num, int da)
num:表示第几个结点,不能为0,因为0结点是头;
da:表示增加的数据;
*******************/
int body_add(int num,int da ) //在中间增加结点函数;
{
	struct data* p=head;
	for (int i = 0; i < num; i++)  //获取在几个结点的后面增加结点数据
	{
		p=p->next;
	}
	p1 = (struct data*)malloc(sizeof(struct data)); //重新给p1开辟新空间;
	p1->num = da;  //给新的结点设置数据;
	p1->next = p->next; //把当前结点指向下一个结点的指针,赋值给结构体指针p1指向的下一个结点的指针;
	p->next = p1;       //然后把结构体p1指针内存地址赋值给当前结点结构体指针指向的下一个结点的指针;
	printf("********************body_add执行成功!*************************\n");
	Inquire();//遍历输出
	return 0;
}

int tail_add(int da) //在尾的后面增加结点函数;
{
	p1 = (struct data*)malloc(sizeof(struct data));  //重新给p1开辟新空间;
	p1->num = da;          //给指针p1指向的结构体成员 num 赋值;
	p1->next = NULL;       //把指针p1的地址赋值给 指针p2指向的结构体成员指针next;
	tail->next = p1;       //把结构体指针p1赋值给尾结构体指针指向的下一个结点;
	tail = p1;				//然后在把p1赋值给尾部指针;
	printf("********************tail_add执行成功*************************\n");
	Inquire();//遍历输出
	return 0;
}

void Inquire() //遍历输出
{
	struct data* p = head;
	printf("当前链表中的数据是:");
	while (p != NULL)         //知道结点指向的指针为NULL
	{
		printf("%d,", p->num);  //输出p指向的结点的数据;
		p = p->next;   //使p指向下一结点;
	}
	printf("\n");
}


int head_dele()  //在头的前面删除结点函数;
{
	p1 = head;  //把头指针内存地址赋值给p1指针
    head=head->next;  //把头指针指向的下一个结点赋值给head指针	
	free(p1);  //释放p1指针分配的内存空间;
	p1 = NULL;
	printf("********************head_dele执行成功!*************************\n");
	Inquire();//遍历输出
	return 0;
}
int body_dele(int num)  //在中间删除结点函数;
{
	struct data* p = head;
	for (int i = 0; i < num; i++)  //获取在几个结点的后面增加结点数据
	{
		p = p->next;
	}
	p1 = p->next;  //把当前结点的下一个结点内存地址赋值给指着p1
	p->next=p->next->next; //把当前结点的下下结点内存地址赋值给下结点内存地址
	free(p1);  //释放p1指针分配的内存空间;
	printf("********************body_dele执行成功!*************************\n");
	Inquire();//遍历输出
	return 0;
}
int tail_dele()  //在尾的后面删除结点函数;
{	
	struct data* p = head,*pt=head;
	pt = pt->next;
	pt = pt->next;
	while (pt != NULL)      //知道结点指向的指针为NULL
	{
		p = p->next;   //使p指向下一结点;
		pt = pt->next; //使pt指向下一结点;
	}  //当pt指向NULL时p指向的下一个结点为尾结点前面的一个结点
	p1 = p->next; 
	tail = p;
	free(p1);//释放p1指针分配的内存空间;
	p->next = NULL;
	printf("********************tail_dele执行成功!*************************\n");
	Inquire();//遍历输出
	return 0;
}

int head_change(int da)  //更改头结点函数;
{
	head->num =da;  //更改头结点的数据	
	printf("********************head_change执行成功!*************************\n");
	Inquire();//遍历输出
	return 0;
}
int body_change(int num, int da)  //更改中间结点函数;
{
	struct data* p = head;
	for (int i = 0; i < num; i++)  //获取在几个结点的后面增加结点数据
	{
		p = p->next;
	}
	p->num = da; //更改当前结点的数据
	printf("********************body_change执行成功!*************************\n");
	Inquire();//遍历输出
	return 0;
}
int tail_change(int da)  //更改尾结点函数;
{
	tail->num = da;       //更改尾结点的数据;
	printf("********************tail_change执行成功!*************************\n");
	Inquire();//遍历输出
	return 0;
}

void examine(int num) //查结点
{
	struct data* p = head;
	for (int i = 0; i < num; i++)  //获取在几个结点的后面增加结点数据
	{
		p = p->next;
	}
	printf("************************查询成功!*********************\n");
	printf("当前结点的数据是:%d\n",p->num);
}

编译运行如下所示:
在这里插入图片描述

四、总结

在对数据存储中链表有着广泛的应用。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/3476.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Python解题 - CSDN周赛第40期

上期问哥没参加&#xff0c;但从赛后大家的反馈来看&#xff0c;又出现了数据上的bug&#xff0c;使用 python 的朋友会遇到第二个用例的柱子高度数组长度不够&#xff0c;200根柱子&#xff0c;只有179个数据&#xff0c;这让人怎么玩&#xff1f;但是用C的选手就没有这个问题…

面试官:vue2和vue3的区别有哪些

目录 多根节点&#xff0c;fragment&#xff08;碎片&#xff09; Composition API reactive 函数是用来创建响应式对象 Ref toRef toRefs 去除了管道 v-model的prop 和 event 默认名称会更改 vue2写法 Vue 3写法 vue3组件需要使用v-model时的写法 其他语法 1. 创…

提升网站性能:Nginx五种高效负载均衡策略

前言 本文收录于我是沐风晓月的csdn专栏《linux基本功-系统服务实战》&#xff0c; 关于nginx的系列后面会汇总起来&#xff0c;关注我&#xff0c;一起学习与成长。 本专栏写作的过程中&#xff0c;联合了csdn几位大佬&#xff0c;目前正在整理更新目录&#xff0c;力争让大…

多线程代码案例-阻塞队列

hi,大家好,今天为大家带来多线程案例--阻塞队列 这块知识点也很重要,要好好掌握呀~~~ &#x1f338;&#x1f338;&#x1f338;&#x1f338;&#x1f338;&#x1f338;&#x1f338;&#x1f338;&#x1f338;&#x1f338;&#x1f338;&#x1f338;&#x1f338;&#x…

【蓝桥杯_练习】

蓝桥杯1.创建工程2.LED灯点亮led.c3.LCD液晶屏显示lcd.c4.定时器按键单机interrupt.hinterrupt.cman.c5.定时器&#xff08;长按键&#xff09;interrupt.hinterrupt.cmain.c6.PWMmain.c7.定时器-输入捕获&#xff08;频率&#xff0c;占空比测量&#xff09;interrupt.cmain.c…

中科亿海微FPGA应用(一、点灯)

1.软件&#xff1a; https://download.csdn.net/download/weixin_41784968/87564071 需要申请license才能使用&#xff1a;软件试用申请_软件试用申请_中科亿海微电子科技&#xff08;苏州&#xff09;有限公司 2.开发板&#xff1a; 芯片EQ6HL45&#xff0c;42.5k LUT。 3…

移植RK3568的串口

文章目录 前言一、代码位置二、硬件原理图三、修改设备树四、关闭串口调试功能总结前言 本文主要讲解如何移植RK3568的串口 提示:以下是本篇文章正文内容,下面案例可供参考 一、代码位置 drivers/tty/serial/8250/8250_core.c drivers/tty/serial/8250/8250_dma.c dma实现…

TCP协议详解

1.TCP的准备条件在古代的时候&#xff0c;古人们经常写书信进行交流&#xff0c;写书信的前提是你要知道这份信是要寄给谁在网络中&#xff0c;我们通过ip端口号找对目标对象&#xff0c;但是现在网站一般会对ip端口注册一个域名&#xff0c;所以我们一般就是对域名进行查找&am…

mysql的limit查询竟然有坑?

背景 最近项目联调的时候发现了分页查询的一个bug&#xff0c;分页查询总有数据查不出来或者重复查出。 数据库一共14条记录。 如果按照一页10条。那么第一页和第二页的查询SQL和和结果如下。 .png) 那么问题来了&#xff0c;查询第一页和第二页的时候都出现了11,12,13的记录…

又一款全新的基于 GPT4 的 Python 神器Cursor,关键还免费

chartgpt大火之后&#xff0c;随之而来的就是一大类衍生物了。 然后&#xff0c;今天要给大家介绍的是一款基于GPT4的新一代辅助编程神器——Cursor。 它最值得介绍的地方在于它免费&#xff0c;我们可以直接利用它来辅助我们编程&#xff0c;真正做到事半功倍。 注意&#…

大数据项目之数仓相关知识

第1章 数据仓库概念 数据仓库&#xff08;DW&#xff09;: 为企业指定决策&#xff0c;提供数据支持的&#xff0c;帮助企业&#xff0c;改进业务流程&#xff0c;提高产品质量等。 DW的输入数据通常包括&#xff1a;业务数据&#xff0c;用户行为数据和爬虫数据等 ODS: 数据…

十二届蓝桥杯省赛c++(下)

1、 拿到题目一定要读懂题意&#xff0c;不要看到这题目就上来模拟什么闰年&#xff0c;一月的天数啥的。这个题目问你当天的时间&#xff0c;就说明年月日跟你都没关系&#xff0c;直接无视就好了。 #include <iostream> #include <cstring> #include <algori…

Nginx 教程-动静分离

一、Nginx 动静分离理论1、概念今天学习和梳理Nginx动静分离&#xff0c;动静分离是将网站静态资源&#xff08;HTML&#xff0c;JavaScript&#xff0c;CSS&#xff0c;img等文件&#xff09;与后台应用分开部署&#xff0c;之所以要进行动静分离&#xff0c;其一为了提高前端…

Qt示例3:用Qt画一个温度计

示例1 以下是用Qt绘制一个简单的温度计的示例代码&#xff1a; #include <QPainter> #include <QWidget> #include <QApplication> class Thermometer : public QWidget { public:Thermometer(QWidget *parent 0); protected:void paintEvent(QPaintEvent …

戴眼镜检测和识别1:戴眼镜检测数据集(含下载链接)

戴眼镜检测和识别1&#xff1a;戴眼镜检测数据集(含下载链接) 目录 戴眼镜检测和识别1&#xff1a;戴眼镜检测数据集(含下载链接) 1. 前言 2.Eyeglasses-Dataset数据集说明 3.Eyeglasses-Dataset数据集下载 4.戴眼镜检测和识别&#xff08;Python版本&#xff09; 5.戴眼…

JavaWeb——线程安全问题的原因和解决方案

目录 一、线程不安全的原因 1、抢占式执行、随机调度 2、多线程同时修改同一个变量 3、修改操作不是原子的 4、内存可见性 5、指令重排序 二、解决方法 1、使用synchronized方法加锁 &#xff08;1&#xff09;、定义 &#xff08;2&#xff09;、使用 &#xff08;3…

Github ChatGPT-Web:了解最新AI技术的前沿应用!

近年来OpenAI的ChatGPT模型在自然语言处理领域取得了很大的进展&#xff0c;并且已经在全球范围内得到了广泛的应用和普及。ChatGPT不仅可以用于生成对话和文本摘要等任务&#xff0c;还可以用于机器翻译、问答系统、情感分析等多个领域。ChatGPT已经成为自然语言处理领域的一个…

基于51单片机的自动打铃打鸣作息报时系统AT89C51数码管三极管时钟电路

wx供重浩&#xff1a;创享日记 对话框发送&#xff1a;单片机打铃 获取完整无水印论文报告说明&#xff08;含源码程序、电路原理图和仿真图&#xff09; 本次设计中的LED数码管电子时钟电路采用24小时制记时方式,本次设计采用AT89C51单片机的扩展芯片和6个PNP三极管做驱动&…

【C语言蓝桥杯每日一题】——跑步锻炼

【C语言蓝桥杯每日一题】—— 跑步锻炼&#x1f60e;前言&#x1f64c;排序&#x1f64c;总结撒花&#x1f49e;&#x1f60e;博客昵称&#xff1a;博客小梦 &#x1f60a;最喜欢的座右铭&#xff1a;全神贯注的上吧&#xff01;&#xff01;&#xff01; &#x1f60a;作者简介…

使用chatGPT实现数字自增动画

num-auto-add&#xff1a;数字自增动画 序言 我们经常在一些好的网站上遇到数字自增的动画效果&#xff0c;为用户提供了更加丰富的交互体验&#xff0c;看起来非常酷。 我之前也有写过&#xff0c;为了方便以后使用&#xff0c;打算将它优化&#xff0c;并上传到npm中。 首…