双向链表<数据结构 C版>

news2024/9/20 22:48:01

目录

关于链表的分类

 双向链表结构体

初始化

尾插

头插

打印

判断是否为空

尾删

头删

查找

指定位置之后的插入

指定位置的删除

销毁


关于链表的分类

根据链表的三大特性,单向or双向、带头or不带头、循环or不循环,可将链表分为2*2*2,8种链表,前面我们已经实现了单链表,即:不带头单向非循环链表,它的结构简单,不常用于单独存储数据,而是作为其他数据结构的子结构。

实际运用中,只有单链表(不带头单向非循环链表)和双向链表(带头双向循环链表)运用最多,带头双向循环链表,结构最复杂,常常运用于单独存储数据,使用的链表结构也几乎都是双向带头链表。

附上一张bit课件的图:


 双向链表结构体

放一张bi课件的图片很形象:

//重定义一下类型,方便统一修改
typedef int LNDataType;

typedef struct ListNode {
	LNDataType data;//数据
	struct ListNode* prev;//前一个结点
	struct ListNode* next;//后一个结点
}LN;

初始化

双向链表的初始化,应创建一个哨兵结点(也称头结点),它存放的数据是无效数据(假定-1),

所以我们先实现一个创建单节点的函数:

//创建新节点
LN* LNBuyNode(LNDataType x) {
	LN* node = (LN*)malloc(sizeof(LN));//开辟空间
	if (!node) {//判断为空
		perror("malloc fail!");
		exit(1);
	}
	node->data = x;//传入数据
	node->next = node->prev = node;
	//双向循环链表单节点也应满足循环,不能初始化为NULL;
	return node;
}

接着我们就可方便地调用,创建一个哨兵结点:

//初始化
void LNInit(LN** pphead) {
	assert(pphead);
	*pphead=LNBuynode(-1);
}

尾插

//尾插
void LNPushBack(LN* phead, LNDataType x) {
	assert(phead);
	LN* node = LNBuyNode(x);//创建新结点
	node->next = phead;//先对新申请的结点参数进行操作,防止对原链表造成改变
	node->prev = phead->prev;

	phead->prev->next = node;//更改尾结点的next参数的指向
	phead->prev = node;//更改头结点prev结点的指向
}

运行测试一下:


头插

注意头插的操作是在哨兵位后插入,双向链表为空的情况也是只剩下哨兵位,因为哨兵位并没有存储有效数据。

//头插
void LNPushFront(LN* phead, LNDataType x) {
	assert(phead);
	LN* node = LNBuyNode(x);//创建新结点
	node->next = phead->next;//先对新申请的结点参数进行操作,防止对原链表造成改变
	node->prev = phead;

	
	phead->next->prev = node;//更改头结点的下一个结点的prev指向
	phead->next = node;//更改头结点的next指向
}

运行测试一下:


打印

//打印
void LNPrint(LN* phead) {
	assert(phead);
	LN* pcur = phead->next;
	//因为头结点内存储的是无效数据,所以我们让它指向下一个结点
	while (pcur!=phead) {//与头结点相遇说明我们已经遍历完整个链表了
		printf("%d->", pcur->data);
		pcur = pcur->next;
	}
	printf("\n");
}

判断是否为空

双向链表为空的情况是只有一个哨兵结点而不是NULL

//判断是否为空
bool LNEmpty(LN* phead) {
	assert(phead);
	return phead->next == phead;
}

尾删

//尾删
void LNPopBack(LN* phead) {
	assert(phead);
	assert(!LNEmpty(phead));//删除操作至少有一个有效数据
	//LNEmptys是空返回true,取反保证不为空
	LN* del = phead->prev;//保存要删除的结点
	phead->prev = del->prev;//对要受影响的结点的参数进行更改
	phead->prev->next = phead;

	free(del);//释放掉该地址
	del = NULL;
}

运行测试一下:


头删

//头删
void LNPopFront(LN* phead) {
	assert(phead);
	assert(!LNEmpty(phead));
	LN* del = phead->next;//保存要删除的结点
	phead->next = del->next;//对要受影响的结点的参数进行更改
	phead->next->prev = phead;

	free(del);//释放掉该地址
	del = NULL;
}

运行测试一下:


查找

因为后面涉及到任意位置的操作,所以这里要写一个查找方法:

//查找
LN* LNFind(LN* phead, LNDataType x) {
	assert(phead);
	assert(!LNEmpty(phead));
	LN* pcur = phead->next;
	while (pcur!=phead) {
	//判断条件为!=phead,遇到哨兵位说明已经遍历完
		if (pcur->data == x) {
			return pcur;
		}
		pcur = pcur->next;
	}
	return NULL;
}

运行测试一下:


指定位置之后的插入

//指定位置之后的插入
void LNInsert(LN* pos, LNDataType x) {
	assert(pos);
	LN* node = LNBuyNode(x);
	node->next = pos->next;//先对要受影响的结点的参数进行更改
	node->prev = pos;

	pos->next->prev = node;//更改pos结点的后一个结点的prev参数
	pos->next = node;//更改pos结点的next参数
}

运行测试一下:


指定位置的删除

//任意位置的删除
void LNErase(LN* pos) {
	assert(pos);
	pos->next->prev = pos->prev;
	pos->prev->next = pos->next;
	free(pos);
	pos = NULL;
}

运行测试一下:


销毁

//销毁
void LNDestory(LN** phead) {
	assert(phead && *phead);
	LN* pcur = (*phead)->next;
	while (pcur != *phead) {
		LN* next = pcur->next;
		free(pcur);
		pcur = next;
	}
	free(*phead);
	*phead =pcur= NULL;
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

利用ascp下载SRA Explorer中转录组数据

最近在windows系统里下载了MobaXterm可以远程登入服务器,处理RNA的数据,需要从NCBI数据库上下载数据。本文提供用虚拟机ubuntu或者linux系统下载Aspera的方法和问题解决,以及从NCBI上批量下载数据库、最后得到一个项目里的所有fastq文件。 A…

前端:Vue学习-2

前端:Vue学习-2 1. vue的生命周期2. 工程化开发和脚手架Vue CLI2.1 组件化开发2.2 scoped解决样式冲突2.3 data是一个函数2.4 组件通信2.5 非父子通信- event bus事件,provide&inject 3.v-model原理->实现父子组件双向绑定4. sync 修饰符->实现…

谷粒商城实战笔记-42-前端基础-Vue-生命周期和钩子函数

下面是Vue官网关于生命周期及不同阶段的钩子函数的图示。 Vue 实例生命周期是指从一个组件被创建到最终被销毁的整个过程。 在这一过程中,Vue 提供了一系列的“钩子”函数,在生命周期的不同阶段执行自定义的代码。 以下是 Vue 对象生命周期的主要阶段…

C语言实现二叉树以及二叉树的详细介绍

目录 1.树概念及结构 1.1树的概念 1.2树的相关概念 1.3树的表示 2.二叉树概念及结构 2.1二叉树的概念 2.2特殊的二叉树 2.3二叉树的性质 2.4二叉树的存储结构 3.二叉树顺序结构--特殊的二叉树--堆及其实现 3.1堆的概念及结构 3.2堆的实现 3.2.1堆的结构 3.2.2堆…

filebeat生产环境配置

配置文件属性 生产配置 filebeat.inputs: - type: logenabled: truepaths: - /tmp/logs/*.log- /var/log/system.log- /var/log/wifi.logsymlinks: truejson.keys_under_root: truejson.message_key: xxxjson.add_error_key: true# 如果想卡部分日志,比如用时间作…

Monaco 使用 HoverProvider

Monaco 中自定义 Hover,Hover 效果是指当鼠标移动文字上展示出提示效果,就像页面上的 Tooltip 效果。最终页面的显示效果如下: 通过 registerHoverProvider 注册 Hover 触发时的处理方法 接口中提供了 4 个参数,前两个参数比较重…

python学习之闭包与装饰器

一、闭包 闭包允许一个函数访问并操作函数外部的变量(即父级作用域中的变量),即使在该函数外部执行。 特性: (1)外部函数嵌套内部函数。 (2)外部函数可以返回内部函数。 (3)内部函数可以访问外部函数的局部变量。 def out()…

【jmeter边界值提取器】

【目的】 从响应头中取token的内容 【方案】 使用后置处理器-边界值提取器 【组件路径】HTTP请求->右键添加->后置处理器->边界提取器 用途:边界提取器(Boundary Extractor)是放在Sample请求之后执行的,用于获取左右边界中间的所有字符&#xf…

Windows配置Qt+VLC

文章目录 前言下载库文件提取文件编写qmakeqtvlc测试代码 总结 前言 在Windows平台上配置Qt和VLC是开发多媒体应用程序的一个重要步骤。Qt作为一个强大的跨平台应用开发框架,为开发人员提供了丰富的GUI工具和库,而VLC则是一个开源的多媒体播放器&#x…

基于STM32的农业大棚温湿度采集控制系统的设计

目录 1、设计要求 2、系统功能 3、演示视频和实物 4、系统设计框图 5、软件设计流程图 6、原理图 7、主程序 8、总结 🤞大家好,这里是5132单片机毕设设计项目分享,今天给大家分享的是智能教室。 设备的详细功能见网盘中的文章《8、基…

451.根据字符出现频率排序(中等)

451.根据字符出现频率排序(中等) 1. 题目描述2.详细题解3.代码实现3.1 Python3.2 Java 1. 题目描述 题目中转:451.根据字符出现频率排序(中等) 2.详细题解 题目: 347. 前 K 个高频元素(中等&am…

2月科研——arcgis计算植被差异

ArcGIS中&#xff0c;设置高于或低于某个值的像元为 -9999&#xff0c;然后将这些地方设为空——目的&#xff1a;去除异常值和黑色背景值 Con(("T_std ano7.tif" > 2) | ("T_std ano7.tif" < - 2), - 9999,"T_std ano7.tif") SetNull(&…

数学基础【俗说矩阵】:初等矩阵和矩阵的初等行变化关系推导

初等矩阵和矩阵的初等行变换 初等矩阵 矩阵的初等行变换 对单位阵E进行一次初等行变化得到的阵叫做初等阵。 这里只能进行一次初等行变换。 置换阵 给矩阵【左乘】一个【置换阵】&#xff0c;相当与对该矩阵进行了一次【置换阵】对应的【置换】初等行变换&#xff1b; 数…

爬取百度图片,想爬谁就爬谁

前言 既然是做爬虫&#xff0c;那么肯定就会有一些小心思&#xff0c;比如去获取一些自己喜欢的资料等。 去百度图片去抓取图片吧 打开百度图片网站&#xff0c;点击搜索xxx&#xff0c;打开后&#xff0c;滚动滚动条&#xff0c;发现滚动条越来越小&#xff0c;说明图片加载…

Wordpress文章分享到微信朋友圈不带图像问题的解决

目录 一、插件选择 二、插件安装 三、获取微信公众号管理信息 1、登录微信公众平台 2、配置“JS接口安全域名” 3、至安全中心设置“IP白名单” 4、获得“开发者ID(AppID)” 5、获得“开发者密码(AppSecret)” 四、配置插件 五、验证 WordPress装配好后&#xff0c;可…

10.11和10.8那个大(各种ai的回答)

问题&#xff1a;10.11和10.8两个数哪个大 腾讯混元 ✔️ chatGPT ❎ 通义千问 ❎ 文心一言 ✔️ 智谱清言 ❎ 讯飞星火 ✔️ C知道 ❎ 豆包 ✔️

matlab 声音信号希尔伯特黄变换

1、内容简介 略 91-可以交流、咨询、答疑 2、内容说明 略 Hilbert-Huang变换&#xff08;HHT&#xff09;是一种基于经验的数据分析方法 方法。它的扩展基础是自适应的&#xff0c;因此它可以从非线性和非平稳过程中产生具有物理意义的数据表示。这个 适应性的优势是有代价…

代码随想录第二天 | 数组 | 双指针法、滑动窗口、螺旋数组

来源 代码随想录数组第二天 有序数组的平方 数组其实是有序的&#xff0c; 只不过负数平方之后可能成为最大数了。 那么数组平方的最大值就在数组的两端&#xff0c;不是最左边就是最右边&#xff0c;不可能是中间。 此时可以考虑双指针法了&#xff0c;i指向起始位置&…

matlab永磁同步电机反馈试验装置的设计和永磁同步电机仿真

1、内容简介 略 85-可以交流、咨询、答疑 2、内容说明 略 摘要&#xff1a;得益于电力电子器件及控制技术的高速发展&#xff0c;使得电机的应用越来越方便及精确&#xff0c;适应了实际应用对电机性能及质量提出的更高要求和标准。同时电机测试技术也因为电力电子技术的发…

c++ primer plus 第16章string 类和标准模板库, 16.3.3 对矢量可执行的其他操作

c primer plus 第16章string 类和标准模板库, 16.3.3 对矢量可执行的其他操作 c primer plus 第16章string 类和标准模板库, 16.3.3 对矢量可执行的其他操作 文章目录 c primer plus 第16章string 类和标准模板库, 16.3.3 对矢量可执行的其他操作16.3.3 对矢量可执行的其他操作…