【数据结构】带头双向循环链表及其实现

news2024/11/15 9:26:38

目录

1.带头双向循环链表

2.带头双向循环链表实现

2.1初始化

2.2销毁

2.3头插

2.4链表打印

2.5头删数据

2.6尾插数据

2.7尾删数据

2.8链表判空 

2.9查找一个数据

2.10在pos位置前插入数据

2.11删除pos位置

2.12求链表的长度

2.顺序表和链表的比较


1.带头双向循环链表

我们已经实现了无头单向循环链表

带头双向链表结构如下:

对于无头单向非循环链表,其具有以下特点:

  • 第一个节点即为存储有效数据的节点
  • 每个节点有包括数据域和指针域,这个指针指向下一个节点
  • 空链表为NULL

对于带头双向循环链表,其具有以下特点

  • 第一个节点为哨兵头节点,其数据域不存储有效数据
  • 每个节点包含数据域和两个指针域prev和next,prev指针指向后一个节点,next指针指向前一个节点,对于哨兵头节点,其prev指针指向链表的尾节点,对于尾节点,其next指针指向哨兵头节点,因此形成了一个循环的结构
  • 空链表时链表包含一个哨兵头节点,如下图:

2.带头双向循环链表实现

2.1初始化

对于一个带头双向循环链表,初始化后其为有一个哨兵头节点的结构

即初始化需要动态开辟一个节点作为哨兵头节点,其具有以下结构

//初始化
LTNode* ListInit(LTNode** pphead)
{
	LTNode* guard = (LTNode*)malloc(sizeof(LTNode));//哨兵头节点
	if (guard == NULL)
	{
		perror("malloc fail");
		exit(-1);
	}
	else
	{
		guard->next = guard;
		guard->prev = guard;
		return guard;
	}
}

2.2销毁

因为链表所有节点的空间都是动态开辟的,因此对链表进行操作后,为了避免内存泄漏,需要释放这些节点所占用的空间,销毁链表遍历释放每个节点即可,需要注意哨兵头节点也是动态开辟的空间,也需要释放

//销毁
void ListDestroy(LTNode* phead)
{
	assert(phead);
	//遍历释放每个节点
	LTNode* cur = phead->next;
	while (cur != phead)
	{
		LTNode* next = cur->next;//保存下一个节点
		free(cur);
		cur = next;
	}
	//释放哨兵头节点
	free(phead);
	phead = NULL;//参数为一级指针,形参的改变不影响实参
}

2.3头插

头插数据有两种情况:

1️⃣空链表时头插

2️⃣非空链表时头插

由上图可以发现:由于带头双向循环链表结构的特殊性,空链表头插和非空链表头插时操作相同

//创建节点
LTNode* BuyNode(LTDataType x)
{
	LTNode* newnode = (LTNode*)malloc(sizeof(LTNode));
	if (newnode == NULL)
	{
		perror("malloc fail");
		exit(-1);
	}
	newnode->data = x;
	newnode->prev = newnode->next = NULL;
	return newnode;

}
//头插数据
void ListPushFront(LTNode* phead, LTDataType x)
{
	assert(phead);
	LTNode* newnode = BuyNode(x);
	LTNode* next = phead->next;
	phead->next = newnode;
	newnode->prev = phead;
	newnode->next = next;
	next->prev = newnode;

}

2.4链表打印

为了方便调试,可以编写打印函数展示我们所创建的链表,遍历打印每个节点的数据域即可

//打印链表
void ListPrint(LTNode* phead)
{
	assert(phead);
	LTNode* cur = phead->next;
	while (cur != phead)
	{
		printf("%d<=>", cur->data);
		cur = cur->next;
	}
	printf("\n");
}

2.5头删数据

头删数据需要判断链表是否为空,空链表则不能进行数据的删除

特殊情况分析:只有一个节点时头删

 由上图可以发现:仅有一个节点时的头删操作和一般情况下头删操作步骤相同

//头删数据
void ListPopFront(LTNode* phead)
{
	assert(phead);
	//空链表则不能删除
	assert(!ListEmpty(phead));
	LTNode* first = phead->next;//first为第一个有效数据节点
	phead->next = first->next;
	first->next->prev = phead;

	free(first);
	first = NULL;

}

2.6尾插数据

尾插数据有两种情况:

1️⃣空链表时尾插

2️⃣非空链表时头插

由上图可以发现:空链表尾插和非空链表尾插时操作相同,所以不用分情况讨论

//尾插数据
void ListPushBack(LTNode* phead, LTDataType x)
{
	assert(phead);
	LTNode* tail = phead->prev;//tail为原尾节点
	LTNode* newnode = BuyNode(x);
	tail->next = newnode;
	newnode->prev = tail;
	phead->prev = newnode;
	newnode->next = phead;
}

2.7尾删数据

尾删数据需要判断链表是否为空,空链表则不能进行数据的删除

非空链删除

特殊情况分析:只有一个节点时尾删

 由上图可以发现:仅有一个节点时的尾删操作和一般情况下尾删操作步骤相同

//尾删数据
void ListPopBack(LTNode* phead)
{
	assert(phead);
	LTNode* tail = phead->prev;//tail为尾节点
	phead->prev = tail->prev;
	tail->prev->next = phead;
}

2.8链表判空 

当链表中只有哨兵头节点时,链表即为空

//判空
bool ListEmpty(LTNode* phead)
{
	assert(phead);
	return phead->next == phead;
}

2.9查找一个数据

从存储有效数据的第一个节点开始遍历链表,查找所给数据

如果找到了,则返回该节点的地址,返回地址也可以对该节点进行修改

遍历结束,没找到,则返回NULL

//查找一个数据
LTNode* ListFind(LTNode* phead, LTDataType x)
{
	assert(phead);
	//遍历查找
	LTNode* cur = phead->next;
	while (cur != phead)
	{
		if (cur->data == x)
		{
			return cur;//返回节点地址,可以进行修改
		}
		cur = cur->next;
	}
	return NULL;

}

2.10在pos位置前插入数据

在pos之前插入数据,需要直到pos前一个节点的地址。在带头双向循环链表中,pos节点中prev指针域存储了前一个节点的地址,使插入数据更加方便,步骤如下:

特殊情况:空链表时,只能在哨兵头节点之前插入,且步骤与上述相同

需要函数调用者保证pos的有效性

//在pos之前插入
void ListInsert(LTNode* pos, LTDataType x)
{
	assert(pos);
	LTNode* front = pos->prev;
	LTNode* newnode = BuyNode(x);
	front->next = newnode;
	newnode->prev = front;
	newnode->next = pos;
	pos->prev = newnode;
}

2.11删除pos位置

pos节点中既存储了其前一个节点的位置,又存储了其后一个节点的位置

删除pos位置:链表不为空时才能删除,链接其前后节点并释放pos节点即可

//删除pos位置
void ListErase(LTNode* phead, LTNode* pos)
{
	assert(phead);
	assert(pos);
	assert(!ListEmpty(phead));
	LTNode* front = pos->prev;
	LTNode* rear = pos->next;
	front->next = rear;
	rear->prev = front;

	free(pos);
	pos = NULL;
}

2.12求链表的长度

遍历链表统计节点个数即可 

//求链表的长度
int ListSize(LTNode* phead)
{
	assert(phead);
	LTNode* cur = phead->next;
	int size = 0;
	while (cur != phead)
	{
		++size;
		cur = cur->next;
	}
	return size;
}

2.顺序表和链表的比较

不同点顺序表链表
存储空间物理上一定连续逻辑上连续,物理上不一定连续
随机访问支持,且时间复杂度为O(1)不支持,访问任意元素的时间复杂度为O(N)
任意位置插入或删除元素需要挪动元素,效率低只需要修改指针的方向,效率较高
插入动态顺序表,空间不够时需要扩容随用随取,不存在容量的概念
应用场景元素高效存储,需要随机访问任意位置频繁插入或删除
缓存利用率

总结:

顺序表的优点:

  • 尾插和尾删的效率高
  • 元素通过下标访问,物理存储空间连续,支持随机访问

顺序表的缺点:

  • 头部插入和中间位置插入需要挪动元素,效率低
  • 扩容操作存在性能消耗和空间浪费

链表的优点:

  • 任意位置插入和删除的时间复杂度为O(N),效率高
  • 按需申请和释放内存,不存在空间浪费

链表的缺点:

  • 不支持随机访问

扩展:

顺序表的优点:相对链表,CPU高速缓存命中率高

CPU执行指令,不会直接访问内存,通常为以下两步:

  1. 数据在三级缓存,命中,直接访问
  2. 若数据不在三级缓存,则先加载到缓存,再访问

顺序表结构使用数组实现:

如上图:要访问0x11223344中的数据,则从0x11223344开始的一段数据都加载进去缓存,加载多少取决于硬件

对于链表,因为其物理存储空间不连续,因此加载到缓存中的这一段数据中可能存在无效数据,导致缓存污染

 

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

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

相关文章

字节前端实习的两道算法题,看看强度如何

最长严格递增子序列 题目描述 给你一个整数数组nums&#xff0c;找到其中最长严格递增子序列的长度。 子序列是由数组派生而来的序列&#xff0c;删除&#xff08;或不删除&#xff09;数组中的元素而不改变其余元素的顺序。例如&#xff0c;[3,6,2,7] 是数组 [0,3,1,6,2,2,7…

使用DataX对MySQL 8.1进行数据迁移

1. 环境准备 1.1 下载DataX 这里采用直接下载的方式&#xff1a;https://datax-opensource.oss-cn-hangzhou.aliyuncs.com/202308/datax.tar.gz&#xff0c;不过这个包是真的有点大。 1.2 安装Python Python下载地址&#xff1a;https://www.python.org/downloads/ 安装的时…

【深度思考】如何优雅的实现脱敏?

最近做了个脱敏的需求&#xff0c;要对系统中的敏感信息&#xff0c;如手机号、车牌号、身份证号、银行卡号等进行脱敏显示。 效果类似下面这样&#xff1a; 简单来说&#xff0c;就是对敏感信息中的某几位进行掩码显示&#xff0c;常见的一般是使用*。 本篇文章就来讲解下在…

解析肖特基二极管NRVBS360BNT3G整流器的优缺点及应用

何为肖特基二极管整流器&#xff1f; 是一种常用的电子器件&#xff0c;用于将交流信号转换为直流信号。它由一个PN结和一个金属接触组成&#xff0c;具有较低的正向压降和快速的开关特性。 在正向偏置下&#xff0c;肖特基二极管具有较低的正向压降&#xff0c;通常为0.3-0.…

如何防范恶意邮件?只要做到这几点

目前&#xff0c;网络钓鱼仍然是企业面临的最大威胁之一&#xff0c;而恶意电子邮件只是网络攻击的起点。一旦进入&#xff0c;威胁行动者就可以展开下一阶段的攻击&#xff0c;例如勒索软件或数据窃取。而这将给被攻击的企业造成巨大的声誉和经济损失&#xff0c;甚至涉及法律…

node升级带来的问题及解决方案(digital envelope routines::unsupported)

由于项目需要将 node版本从16升级到了18&#xff0c;但是原有的老项目还是使用的16的环境&#xff0c;导致在运行老版本的时候出现错误 错误信息如下 Error: error:0308010C:digital envelope routines::unsupportedat new Hash (node:internal/crypto/hash:71:19)at Object.…

文件恢复工具推荐,这4款高效恢复数据!

“有什么好用的文件恢复工具推荐吗&#xff1f;我经常莫名其妙丢失文件&#xff01;而且还是一些很重要的文文件&#xff0c;我都不知道该怎么办了&#xff01;请大家帮帮我&#xff0c;感谢大家&#xff01;” 文件恢复对各位电脑用户来说可能都并不陌生。在使用电脑时&#x…

win | wireshark | 在win上跑lua脚本 解析数据包

前提说明&#xff1a;之前是在linux 系统上配置的&#xff0c;然后现在 在配置lua 脚本 &#xff0c;然后 分析指定协议 的 数据包 其实流程也比较简单&#xff0c;但 逻辑需要缕清来 首先要把你 预先准备的 xxx.lua 文件放到wireshark 的安装文件中&#xff0c;&#xff08;我…

easyexcel poi根据模板导出Excel

1.导入依赖 <dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>3.0.1</version> </dependency>2.代码实现 package com.jiayou.peis.manage.biz.cotroller;import com.alibaba.excel.…

vue2中使用全屏screenfull插件遇到的问题

1、安装screenfull6.0.2后&#xff0c;启动项目。报错 解决方案&#xff1a; 第一种&#xff1a;卸载6.0的版本&#xff0c;安装 screenfull5.1.0 第二种&#xff1a; vue.config.js文件中配置属性 第三种&#xff1a; 安装插件babel/plugin-proposal-nullish-coalescing-o…

c语言练习题40:深入理解字符串常量

深入理解字符串常量 #include<stdio.h> int main() {char str1[] "abcd";char* str2 "abcd";printf("%s\n", str1);printf("%s\n", str2);return 0; } char str1[] "abcd";是用字符串初始化数组。 char* str2 &…

AMBEO 双声道空间音频现已迈进直播制作领域

图片来源&#xff1a;Unsplash&#xff0c;作者&#xff1a;Bence Balla-Schottner AMBEO 双声道空间音频现已迈进直播制作领域 为所有观众解锁更加身临其境的听觉体验 森海塞尔将功能强大的 AMBEO 双声道空间音频技术引入了广播电视直播应用领域&#xff0c;对所有体育赛事广…

ssm实验室开放管理系统源码和论文

ssm实验室开放管理系统源码和论文096 开发工具&#xff1a;idea 数据库mysql5.7 数据库链接工具&#xff1a;navcat,小海豚等 技术&#xff1a;ssm 摘 要 现代经济快节奏发展以及不断完善升级的信息化技术&#xff0c;让传统数据信息的管理升级为软件存储&#xff0c;归…

海外ASO优化之如何分析我们的应用

分析我们的应用在商店中的可见度的当前状况&#xff0c;我们当前使用的关键词是否带来了任何转化&#xff1f;我们在每个关键词查询的搜索结果中排名如何&#xff1f;我们的类别排名如何&#xff1f; 1、将相关的添加到我们的跟踪列表中。 观察每个项目的变化和排名。接下来转…

拧紧燃气“安全阀”!汉威科技为城市生命线构筑安全监控网

防患于未“燃”&#xff0c;拧紧燃气“安全阀”&#xff0c;这是关乎亿万国人生命财产安全的大事&#xff0c;也一直是党和国家重点发力的民生工程。8月11日&#xff0c;国务院安委会正式印发《全国城镇燃气安全专项整治工作方案》&#xff0c;也让液化石油气全链条的安全问题成…

c# 本地化中英文切换

区域 线程默认区域为当前计算机所选区域 设置当前区域&#xff1a; Thread.CurrentThread.CurrentCulture new CultureInfo(“zh-cn”); 获取当前区域&#xff1a; Console.WriteLine(Thread.CurrentThread.CurrentCulture.ToString()); 区域名称&#xff1a; “zh-cn” 中文…

数据驱动的生活:探索未来七天生活指数API的应用

前言 随着科技的不断发展&#xff0c;数据已经成为我们生活中不可或缺的一部分。从社交媒体上的点赞和分享&#xff0c;到电子邮件和搜索引擎的历史记录&#xff0c;数据正在以前所未有的速度积累。而这些数据的利用不仅仅停留在社交媒体或商业领域&#xff0c;它们还可以为我…

高通开发系列 - 5G网络之QTI守护进程服务介绍

By: fulinux E-mail: fulinux@sina.com Blog: https://blog.csdn.net/fulinus 喜欢的盆友欢迎点赞和订阅! 你的喜欢就是我写作的动力! 返回:专栏总目录 目录 代码位置和依赖关系功能介绍代码逻辑讲解外设节点关注的目录socket服务端初始化DPM客户端监听守护关键的数据结构体…

哈希表哈希桶

目录 哈希概念 哈希冲突 哈希函数 一、直接定址法&#xff08;常用&#xff09; 二、除留余数法&#xff08;常用&#xff09; 三、平方取中法 四、折叠法 五、随机数法 六、数字分析法 哈希冲突解决 开散列—— 链地址法&#xff08;拉链法、哈希桶&#xff09; 闭…