双向链表(详解)

在单链表专题中我们提到链表的分类,其中提到了带头双向循环链表,今天小编将详细讲下双向链表。

话不多说,直接上货。

1.双向链表的结构

带头双向循环链表

80f0f03a9fbd4ef6afe363958f5a3f9a.png

注意

这几的“带头”跟前面我们说的“头节点”是两个概念,实际前面的在单链表阶段称呼不严
谨,但是为了更好的理解就直接称为单链表的头节点。
带头链表里的头节点,实际为“哨兵位”,哨兵位节点不存储任何有效元素,只是站在这里“放哨 的”。
“哨兵位”存在的意义: 遍历循环链表避免死循环。

插入操作时,不需要检查是否在头部插入,因为哨兵节点作为头结点,总是存在。

删除操作时,不需要处理删除的是否是头节点的情况,因为哨兵节点不会被删除。

简化了代码,因为不需要为头节点和普通节点编写不同的处理逻辑。
双向链表文字上没有什么好说的,具体主要是代码的实现,有了单链表的基础铺垫,双向链表实现也会轻松很多, 主要是理清楚前后节点的关系。

2.双向链表的实现

我们同样是按照项目的格式。

1.头文件的建立(函数库引入,所需函数的导入) 

#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
//#include <stdbool.h>
typedef int Datatype;
//链表节点创建
typedef struct  ListNode {
	Datatype data;
	struct ListNode* next;
	struct ListNode* prev;
}Node;
//相关函数实现
// 链表初始化,双向链表头节点不能为空
//void LTInit(Node** pphead);
Node* LTInit();
//链表销毁
void LTDestroy(Node* phead);
//链表打印
void LTPrint(Node* phead);
//bool LTEmpty(Node* phead);
//尾插和头插
void LTPushBack(Node* phead, Datatype x);
void LTPushFront(Node* phead, Datatype x);
//尾删和头删
void LTPopBack(Node* phead);
void LTPopFront(Node* phead);
//在pos位置之后插⼊数据
void LTInsert(Node* pos, Datatype x);
//删除指定节点
void LTErase(Node* pos);
//节点找寻,方便指定插入和删除
Node* LTFind(Node* phead, Datatype x);

2.相关函数的实现

2.1 新节点建立

Node* Buynode(Datatype x) {
	Node* node = (Node*)malloc(sizeof(Node));
	if (node == NULL) {
		perror("malloc error");
		exit(1);
	}
	node->data = x;

	node->next = node->prev=node;
	return node;
}

这里为什么节点前后没有指向空指针,因为在后续中链表初始化时,若指向都是空指针,则创建的链表不是双向链表。

在双向链表中,每个节点都有两个指针,一个指向前一个节点(prev),一个指向后一个节点(next)。这样可以实现双向遍历和操作。 当初始化一个双向链表时,需要创建一个头节点(head node),这个头节点不存储实际的数据,只用于标识链表的起始位置。头节点的prev指针和next指针都应该指向自己,这样可以在链表中任意位置插入和删除节点而不需要特殊处理边界情况。

如果初始化时将prev和next指针都指向空,那么在插入和删除节点时就需要特殊处理头节点和尾节点的情况,增加了代码的复杂性。因此,在初始化时将prev和next指针都指向自己是一种简化设计,方便后续操作的方式。

总结来说,prev和next指针不能指向空,是为了简化双向链表的设计和操作。

2.2 链表初始化

Node* LTInit() {
	Node* pphead = Buynode(-1);
	if (pphead == NULL) {
		printf("初始化失败!");
	}
	return pphead;
}

2.3 链表的打印

//链表打印
void LTPrint(Node* phead) {
	Node* pcur = (Node*)malloc(sizeof(Node));
	pcur = phead->next;
	while (pcur!= phead) {
		printf("%d->", pcur->data);
		pcur = pcur->next;
	}
	printf("\n");
}

2.4 尾插和头插

/尾插和头插
void LTPushBack(Node* phead, Datatype x) {
	assert(phead);
	//Node* ptail = (Node*)malloc(sizeof(Node));
	Node* newnode = Buynode(x);
	newnode->prev = phead->prev;//新节点指向原来的尾节点
	newnode->next = phead;
	phead->prev->next = newnode; //让原本的尾节点指向新的节点
	phead->prev = newnode;
}
void LTPushFront(Node* phead, Datatype x) {
	assert(phead);
	Node* newnode = Buynode(x);
	newnode->next = phead->next;
	newnode->prev = phead;

	phead->next->prev= newnode;//两行不能完全交换,若交换
	phead->next = newnode;//phead->next=newnode;newnode->prev=newnode;

}

2.5 尾删和头删

//尾删和头删
void LTPopBack(Node* phead) {
	//链表必须有效且链表不能为空(只有一个哨兵位)
	assert(phead && phead->next!=phead);
	Node* del = phead->prev;
	Node* ptail = del->prev;
	ptail->next = phead;
	phead->prev = ptail;
	free(del);
	del = NULL;
}

void LTPopFront(Node* phead) {
	assert(phead && phead->next != phead);
	Node* del = phead->next;
	phead->next = del->next;
	del->next->prev = phead;
	free(del);
	del = NULL;
}

2.6 节点的找寻

方便在指定的节点前后进行相关操作

Node* LTFind(Node* phead, Datatype x) {
	Node* find = phead->next;
	while (find!=phead) {
		if (find->data == x)
			return find;
		else
		find = find->next;
	}
	return NULL;
}

2.7 指定节点处的操作

//在pos位置之后插⼊数据
void LTInsert(Node* pos, Datatype x) {
	assert(pos);
	Node* newnode = Buynode(x);
	newnode->next = pos->next;
	newnode->prev = pos;
	pos->next->prev = newnode;
	pos->next = newnode;
}

//删除指定节点
void LTErase(Node* pos) {
	assert(pos);
	pos->next->prev = pos->prev;
	pos->prev->next = pos->next;
	free(pos);
	pos = NULL;
}

2.8 链表的销毁

void LTDestroy(Node* phead) {
	assert(phead);
	Node* pcur = phead->next;
	while (pcur->next != phead) {
		Node* next = pcur->next;
		free(pcur);
		pcur = next;
	}
	free(phead);
	phead = NULL;
}

在实现相关函数时,都是从前后节点入手,改变next和prev的指向。

3. 链表的测试

#define _CRT_SECURE_NO_WARNINGS
#include "List.h"
void test1() {
	Node*plist=LTInit();
	//printf("%d", phead->data);
	//头插
	LTPushFront(plist, 1);
	LTPushFront(plist, 2);
	LTPrint(plist);
	//尾插
	LTPushBack(plist, 5);
	LTPushBack(plist, 3);
	//LTPrint(plist);
	
	//尾删
	//LTPopBack(plist, 3);
	//LTPopBack(plist, 5);
	//LTPrint(plist);
	//头删
	//LTPopFront(plist, 1);
	//LTPopFront(plist, 2);
	//LTPrint(plist);
	//节点查找
	Node* find = LTFind(plist, 5);
	if (find == NULL)
		printf("Not Found\n");
	else
		printf("找到了\n");
   //指定插入
	LTInsert(find, 66);
	LTPrint(plist);
   //指定节点删除
	LTErase(find);
	LTPrint(plist);
   //链表销毁
	LTDestroy(plist);
	plist = NULL;
}
int main() {
	test1();
	return 0;
}

3.顺序表和链表的优缺点对比(了解)

a1a0210b061f427e879f8a00415db020.png

看完给小编留下点赞,关注加三连吧!!!

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

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

相关文章

第十三届蓝桥杯决赛(国赛)真题 Java A 组【原卷】

文章目录 发现宝藏【考生须知】试题 A: 火柴棒数字试题 B: 小蓝与钥匙试题 C: 内存空间试题 D: 斐波那契数组试题 E: 交通信号试题 F: 数组个数试题 G: 六六大顺试题 H : \mathrm{H}: H: 选素数试题 I: 图书借阅试题 J \mathrm{J} J : 括号序列树 发现宝藏 前些天发现了一个…

数据分析:基于sparcc的co-occurrence网络

介绍 Sparcc是基于16s或metagenomics数据等计算组成数据之间关联关系的算法。通常使用count matrix数据。 安装Sparcc软件 git clone gitgithub.com:JCSzamosi/SparCC3.git export PATH/path/SparCC3:$PATHwhich SparCC.py导入数据 注&#xff1a;使用rarefy抽平的count ma…

stm32单片机学习路线

第一步 编程及硬件基础知识 1.掌握C语言基础 作为STM32的主要编程语言&#xff0c;C语言的基础知识是必不可少的。建议通过书籍、在线课程或者教学视频系统地学习C语言的基础知识&#xff0c;包括语法、数据类型、函数、指针等。 2.了解电子电路基础 对于单片机开发来说&…

经开区创维汽车车辆交接仪式顺利举行,守护绿色出行助力低碳发展

5月10日&#xff0c;“创维新能源汽车进机关”交车仪式于徐州顺利举行&#xff0c;20辆创维EV6 II正式交付经开区政府投入使用。经开区陈琳副书记、党政办公室副主任张驰主任、经开区公车管理平台苑忠民科长、创维汽车总裁、联合创始人吴龙八先生、创维汽车营销公司总经理饶总先…

OpenHarmony 实战开发——移植通信子系统

通信子系统目前涉及Wi-Fi和蓝牙适配&#xff0c;厂商应当根据芯片自身情况进行适配。 移植指导 Wi-Fi编译文件内容如下&#xff1a; 路径&#xff1a;“foundation/communication/wifi_lite/BUILD.gn” group("wifi") {deps [ "$ohos_board_adapter_dir/ha…

asp.net mvc使用IHttpModule拦截所有请求,包括资源文件

目录 HttpApplication 类 添加App_Code文件夹 MyHttpModel2 Web.config添加配置&#xff0c;在iis模块中生效 项目发布后&#xff0c;察看注册的自定义模块 框架集&#xff1a;.NET Framework 4.7web框架&#xff1a;asp.net mvc 5 HttpApplication 类 HttpApplication 类…

数据库备份与恢复--06---MySQL集群高可用架构之MHA

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 MySQL集群高可用架构之MHA1.什么是MHAMHA&#xff08;MasterHigh Availability&#xff09;是一套优秀的MySQL高可用环境下故障切换和主从复制的软件 &#xff0c;m…

2024最新洗地机选购攻略!分享四款热门洗地机推荐

洗地机可以说是现代家庭生活中一大利器&#xff0c;它能帮我们快速搞定家里的地板清洁工作&#xff0c;省去了自己清洗滚刷的麻烦。不过&#xff0c;当下市面上洗地机品牌种类繁多&#xff0c;价格区间也相差悬殊&#xff0c;要选择一款性价比较高、使用体验较好的洗地机产品&a…

太阳能无人机的多元化应用

随着新能源技术的不断发展和成熟&#xff0c;太阳能在无人机的应用技术已经成熟。太阳能无人机得到了量产和广泛的应用。传统无人机相比&#xff0c;太阳能无人机无需燃油&#xff0c;运行费用低廉&#xff0c;搭载多种高科技设备&#xff0c;能够高效、多元化地采集和分析各类…

AI算法-高数3-导数-求导法则

P16 2.2 求导法则&#xff0c;宋浩老师&#xff1a;2.2 求导法则_哔哩哔哩_bilibili 反函数求导法则&#xff1a; 复合函数求导&#xff1a;剥洋葱法。

蜂群优化算法(bee colony optimization algorithm)

​注意&#xff1a;本文引用自专业人工智能社区Venus AI 更多AI知识请参考原站 &#xff08;[www.aideeplearning.cn]&#xff09; 算法引言 自然界的启发&#xff1a;BSO算法的灵感来自于蜜蜂在自然界中的觅食行为。在自然界中&#xff0c;蜜蜂需要找到花蜜来生存。当一只蜜…

在做题中学习(53):寻找旋转数组中的最小值

153. 寻找旋转排序数组中的最小值 - 力扣&#xff08;LeetCode&#xff09; 解法&#xff1a;O(logn)->很可能就是二分查找 思路&#xff1a;再看看题目要求&#xff0c;可以画出旋转之后数组中元素的大小关系&#xff1a; 首先&#xff0c;数组是具有二段性的(适配二分查…

车载测试系列:UDS诊断服务

UDS诊断服务介绍 UDS&#xff08;Unified Diagnostic Services&#xff0c;统一诊断服务&#xff09;诊断协议诊断测试仪和ECU之间一种通信协议&#xff0c;在ISO14229中规定。UDS被用在几乎所有由OEM一级供应商所制造的新ECU。 诊断工具与车内的所有ECU均有连接&#xff0c;…

IO 5.10

在一个进程中&#xff0c;创建一个子线程。 主线程负责&#xff1a;向文件中写入数据 子线程负责&#xff1a;从文件中读取数据 要求使用线程的同步逻辑&#xff0c;保证一定在主线程向文件中写入数据成功之后&#xff0c;子线程才开始运行&#xff0c;去读取文件中的数据#incl…

bash: docker-compose: 未找到命令

bash: docker-compose: 未找到命令 在一台新的服务器上使用 docker-compose 命令时&#xff0c;报错说 docker-compose 命令找不到&#xff0c;在网上试了一些安装方法&#xff0c;良莠不齐&#xff0c;所以在这块整理一下&#xff0c;如何正确快速的安装 docker-compose cd…

复习了好久的软考中项,现在上半年不考了,该怎么办?

如果有更多学习时间的话&#xff0c;可以考虑报考高级职称&#xff0c;因为高级和中级职称的很多知识点有重叠&#xff0c;只需要再复习一下相关论文就可以了。 从2024年下半年开始&#xff0c;集成考试将采用最新版教材和大纲&#xff0c;与高级职称的新版教材内容相似度很高…

【计算机毕业设计】springboot二手家电管理平台

时代在飞速进步&#xff0c;每个行业都在努力发展现在先进技术&#xff0c;通过这些先进的技术来提高自己的水平和优势&#xff0c;二手家电管理平台当然不能排除在外。二手家电管理平台是在实际应用和 软件工程的开发原理之上&#xff0c;运用java语言以及前台VUE框架&#xf…

JMeter安装及使用

JMeter安装及使用 1.JMete安装 1.1 本地需要有Java8的环境 1.2 下载链接 JMeter官网下载地址 1.3 解压即安装 【&#xff01;&#xff01;命令窗口不可关闭】 2.更换语言 方法1 》Options》Choose Language》Chinese Simplified 方法2 解压的文件》bin》jmeter.propert…

盘点2024年4月Sui生态发展,了解Sui近期成长历程!

2024年4月是Sui的活动月&#xff0c;10–11日聚焦全世界目光的Sui Basecamp会议如约而至&#xff0c;来自65个国家的超过1100人参加了这场在巴黎举办的Sui全球性活动。21日&#xff0c;Sui首届全球黑客松正式开放注册。同时&#xff0c;20日-28日&#xff0c;七天四场大陆地区重…

通用产品发布解决方案(家居分类表设计以及renren代码生成器的使用)

文章目录 1.商品分类表设计1.需求分析2.数据库表设计1.数据库sunliving_commodity&#xff0c;商品分类表commodity_category2.测试数据 2.代码生成器生成crud1.解压到sunliving下并聚合管理1.解压2.修改sunliving的pom.xml进行聚合管理3.刷新maven报错 parent.relativePath4.将…