[数据结构]带头双向循环链表的实现与应用
文章目录
- 一、引言
- 二、链表的基本概念
- 1、链表是什么
- 2、链表与顺序表的区别
- 3、带头双向循环链表
- 三、带头双向循环链表的实现
- 1、结构体定义
- 2、初始化
- 3、销毁
- 4、显示
- 5、数据操作
- 四、分析带头双向循环链表
- 1、存储方式
- 2、优点
- 3、缺点
- 五、总结
- 1、练习题
- 2、源代码
一、引言
链表作为数据结构中的重要一员,在众多应用场景中发挥着关键作用。本文旨在深入探讨带头双向循环链表的基本概念、实现机制及其优缺点,以便读者能够更全面地理解并有效运用这一数据结构。
二、链表的基本概念
1、链表是什么
链表是一种线性数据结构,但与传统的顺序表(例如数组)不同,链表中的元素(节点)在内存中并非连续存储。每个节点包含一个数据域和一个或多个指针域,这些指针域指向链表中的其他节点,从而构成链式结构。
2、链表与顺序表的区别
- 存储方式:顺序表采用连续存储方式,而链表则采用分散存储方式。
- 访问速度:顺序表支持通过索引直接访问元素,因此访问速度较快;而链表则需要从头节点开始顺序遍历,访问速度相对较慢。
- 插入与删除操作:顺序表在插入或删除元素时需要移动大量元素,效率较低;链表则通过修改指针指向即可完成插入和删除操作,效率较高。
- 内存利用率:顺序表在创建时需预先分配连续内存空间,可能导致内存浪费;链表则可根据需求动态分配内存,内存利用率更高。
3、带头双向循环链表
带头双向循环链表是一种特殊的链表结构,具有以下特点:
- 头节点:通常不存储实际数据,仅作为链表的起始点,便于插入和删除操作。
- 双向指针:每个节点包含两个指针,一个指向下一个节点,另一个指向上一个节点,从而支持从任意节点向前或向后遍历链表。
- 循环结构:链表的最后一个节点指向头节点,形成一个环形结构,便于从链表的任意位置开始遍历整个链表。
这种结构使得带头双向循环链表在插入、删除以及遍历操作上具有更高的灵活性和效率。
三、带头双向循环链表的实现
1、结构体定义
在C语言环境中,我们通过定义结构体来构建带头双向循环链表。该结构体融合了数据域与指针域,为链表节点提供了全面的功能支持。以下是一个典型的结构体定义示例:
typedef int DataType;typedef struct ListNode {DataType data;struct ListNode* prev;struct ListNode* next;
}L;
2、初始化
链表初始化是构建带头双向循环链表的首要步骤。此过程涉及头节点的内存分配、指针的初始化设置,以及环形结构的构建。以下是具体的初始化实现代码:
void Init(L** head)
{assert(head != NULL && *head == NULL);L* pos = (L*)malloc(sizeof(L));if (pos == NULL){fprintf(stderr, "内存分配失败");exit(EXIT_FAILURE);}pos->next = pos;pos->prev = pos;pos->data = 0;*head = pos;
}
3、销毁
链表销毁是释放链表所占内存资源的关键步骤。此过程需遍历链表,逐个释放节点的内存,并将头节点指针置为NULL。以下是具体的销毁实现代码:
void Destroy(L** head)
{if (head == NULL)return;L* h = *head;while (*head != h){L* next = (*head)->next;free(*head);*head = next;}*head = NULL;
}
4、显示
链表显示是验证链表正确性的重要手段。此过程通过遍历链表,并打印每个节点的数据部分来实现。以下是具体的显示实现代码:
void Print(L** head, void (*Prin) (DataType))
{assert(head != NULL && Prin != NULL);printf("head->");for (L* i = (*head)->next; i != *head; i = i->next){Prin(i->data);}printf("\n");
}
5、数据操作
void PushFront(L** head, DataType data)
{assert(head != NULL && *head != NULL);L* pos = (L*)malloc(sizeof(L));if (pos == NULL){fprintf(stderr, "内存分配失败");exit(EXIT_FAILURE);}pos->next = (*head)->next;pos->prev = *head;pos->next->prev = pos;pos->data = data;(*head)->next = pos;(*head)->data++;
}void PopFront(L** head)
{assert(head != NULL && *head != NULL && (*head)->next != *head);L* next = (*head)->next;(*head)->next = next->next;next->next->prev = *head;free(next);(*head)->data--;
}void PushBack(L** head, DataType data)
{assert(head != NULL && *head != NULL);L* pos = (L*)malloc(sizeof(L));if (pos == NULL){fprintf(stderr, "内存分配失败");exit(EXIT_FAILURE);}pos->next = *head;pos->prev = (*head)->prev;pos->prev->next = pos;pos->data = data;(*head)->prev = pos;(*head)->data++;
}void PopBack(L** head)
{assert(head != NULL && *head != NULL && (*head)->next != *head);L* prev = (*head)->prev;(*head)->prev = prev->prev;prev->prev->next = *head;free(prev);(*head)->data--;
}L* Find(L** head, DataType data)
{assert(head != NULL && *head != NULL);for (L* i = (*head)->next; i != *head; i = i->next){if (i->data == data)return i;}return NULL;
}void Modify(L** head, L* x, DataType data)
{assert(head != NULL && *head != NULL && x != NULL);for (L* i = (*head)->next; i != *head; i = i->next){if (i == x){i->data = data;return;}}assert(0);
}
四、分析带头双向循环链表
1、存储方式
带头双向循环链表是一种特殊的链表结构,它包含一个头节点,并且该头节点与链表的第一个实际数据节点以及最后一个数据节点之间都通过指针相互连接,形成一个环状结构。每个节点除了存储数据外,还包含两个指针,一个指向前一个节点,另一个指向后一个节点。
2、优点
- 插入和删除操作效率高:由于链表通过指针连接各个节点,因此在进行插入和删除操作时,只需修改相关节点的指针指向即可,无需移动大量数据。这使得插入和删除操作的时间复杂度为O(1),即常数时间。
- 内存利用率高:链表结构允许根据实际需求动态分配内存空间,避免了像数组那样因预先分配过大空间而造成的内存浪费。因此,链表在内存利用方面具有较高的灵活性。
3、缺点
- 访问速度较慢:链表中的数据节点并非连续存储,而是通过指针连接。因此,要访问链表中的某个元素,通常需要从头节点开始遍历链表,直到找到目标节点。这使得访问元素的时间复杂度为O(n),即线性时间,其中n为链表的长度。
- 存储空间较大:链表中的每个节点除了存储数据外,还需要额外存储两个指针(一个指向前一个节点,另一个指向后一个节点)。这增加了节点的存储空间需求,相对于数组等连续存储结构,链表在存储空间方面可能不够紧凑。
五、总结
1、练习题
- 移除链表元素
- 相交链表
- 环形链表 I
- 环形链表 II
- 随机链表的复制
2、源代码
对于带头双向循环链表的源代码我已经开源在Gitee:传送门。
相关文章:

[数据结构]带头双向循环链表的实现与应用
文章目录 一、引言二、链表的基本概念1、链表是什么2、链表与顺序表的区别3、带头双向循环链表 三、带头双向循环链表的实现1、结构体定义2、初始化3、销毁4、显示5、数据操作 四、分析带头双向循环链表1、存储方式2、优点3、缺点 五、总结1、练习题2、源代码 一、引言 链表作…...

商品详情数据API接口开发系列(属性规格详情图sku等)
商品详情数据API接口开发是一个复杂但至关重要的过程,它涉及多个方面,包括属性规格、详情图、SKU等关键信息的处理。以下是对该开发系列中这些关键要素的详细探讨: 一、商品详情数据API接口概述 商品详情数据API接口是指一种编程接口&#x…...

在 Ubuntu 上安装 clang-format-14
在 Ubuntu 上安装 clang-format-14 可以通过以下步骤完成: 1. 添加 LLVM 的官方 APT 仓库 首先,你需要添加 LLVM 的官方 APT 仓库,以便能够安装最新版本的 clang-format。 # 安装必要的依赖 sudo apt update sudo apt install -y wget gnu…...

【优选算法篇】双指针的华丽探戈:深入C++算法殿堂的优雅追寻
文章目录 C 双指针详解:进阶题解与思维分析前言第一章:有效三角形的个数1.1 有效三角形的个数示例 1:示例 2:解法一(暴力求解)解法二(排序 双指针)易错点提示代码解读 第二章&#…...

【springboot入门-mvc常用注解使用方式及原理】
常用注解 PathVariable:用于从URL路径中提取变量。RequestHeader:用于从HTTP请求头中获取数据。ModelAttribute:用于获取请求参数(包括URL参数和POST请求的表单数据),也可以用于将数据绑定到对象上。Reque…...

滚雪球学Redis[4.2讲]:Redis Sentinel 深度解析:工作原理、配置与高可用架构下的故障转移
全文目录: 🎉前言🚦4.2 Redis Sentinel🔄Sentinel的工作原理Sentinel的选举机制 ⚙️Sentinel的配置与使用示例:配置Redis SentinelSentinel自动故障转移过程示例 🧩高可用架构下的故障转移常见问题与优化实…...

Vue3 -- 设置分页,切换分页之后选项仍能保留 控制多个表格的选中不会互相影响
在 Vue 3 中实现分页功能,并确保在切换分页时选中的选项能够保留,同时控制多个表格之间的选中状态不互相影响,可以按照以下步骤进行: 1. 数据结构设计 为每个表格维护独立的选中项和分页状态。可以使用一个对象来存储每个表格的…...

如何在 JSON 中编写“anyOf”语句?
在 JSON 中,anyOf 语句通常用于 JSON Schema(JSON 模式)中,来定义多个可能的模式,表示数据可以匹配多个子模式中的任意一个。这种功能常用于验证 JSON 数据是否符合某一组可能的条件之一。 1、问题背景 问题ÿ…...

python开发环境配置
下载python安装包安装python配置环境变量调整类库下载位置 安装python 安装python是指安装python的基础编译环境及python运行所需的必须资源,类似于安装java的JDK python2与python3差异 进行python安装前,需要先了解python2和python3的差异࿰…...

QT开发--QT SQL模块
第十五章 QT SQL模块 15.1 QT SQL模块概览 Qt SQL模块是Qt框架中操作数据库的组件,提供易用API,支持SQLite、MySQL等多种数据库。它包含数据库驱动与连接功能。 15.1.1 QSqlDatabase 类 在Qt SQL模块中,数据库驱动基于QSqlDriver类…...

如何保证接口幂等性?
一、什么是接口幂等性? 幂等性是指:同一请求,执行很多次,最终结果都一样。 二、为什么会产生接口幂等性问题? 那么,什么情况下,会产生接口幂等性的问题呢? 网络波动, 可能会引起重…...

【9718】基于springboot+vue的生鲜交易系统
作者主页:Java码库 主营内容:SpringBoot、Vue、SSM、HLMT、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、小程序、安卓app等设计与开发。 收藏点赞不迷路 关注作者有好处 文末获取源码 项目描述 生鲜交易管理方面的任务繁琐,以至于交易市场每年都在生…...

Spring循环依赖解决方案
解决方案 使用提前暴露机制三级缓存进行解决 singletonObjects一级缓存,存放完整的 Bean。earlySingletonObjects二级缓存,存放提前暴露的Bean,Bean 是不完整的,未完成属性注入和执行 init 方法。singletonFactories三级缓存(用…...

解决 IntelliJ IDEA 运行时 “Command line is too long“ 问题
文章目录 文章标题:解决 IntelliJ IDEA 运行时 "Command line is too long" 问题简介问题描述解决方案代码示例代码示例1:使用JAR Manifest代码示例2:使用Classpath File代码示例3:优化项目依赖 结论进一步的资源 文章标…...

鸿蒙网络编程系列5-TCP连接超时分析
1. TCP连接超时简介 TCP是面向连接的协议,通过三次握手建立连接,但是,在建立连接的过程中对方有可能没有响应,这时候发起连接的一方会重试,如果重试多次仍然没有响应,就会触发超时,从而导致连接…...

金蝶云星空移动字段后关闭页面后重新打开无效
有同事反馈,单据的明细字段里面移动了字段,然后退出,其他字段都能按最后排版的位置显示,有个别字段始终无法按照排版的位置显示。 只需要打开BOS平台,找到对应字段,然后更改可见性。...

幂律分布笔记
一、幂律分布的数据拟合 数据分箱: 所谓分箱就是对原始数据进行分组,然后对每一组内的数据进行平滑处理。常见的分箱方式主要有等深分箱、等宽分箱、用户自定义等 对数分箱: 对原数据进行分箱,第i个箱的宽度为bi,b…...

一些NLP代表性模型
(一)BERT 由Bidirectional Encoder Representations from Transformers的首字母组成,是encoder-only结构类型的代表。 模型分预训练和微调两步,预训练任务有两类:masked language model(MLM)、next sentence predict…...

低代码移动端开发:未来的趋势与挑战
什么是低代码移动端开发? 低代码移动端开发平台允许开发者通过可视化界面和少量编码来构建应用程序。相较于传统的代码开发,低代码平台大大降低了技术和学习门槛,使非专业开发人员也能参与到移动应用的开发过程中。 低代码移动端开发的优势 …...

【Linux】嵌入式Linux系统的组成、u-boot编译
Linux—嵌入式Linux系统的组成、u-boot编译 前言一、嵌入式Linux系统的组成1.1 嵌入式Linux系统和PC完整的操作系统的对比如下:1.2 PC机—Windows系统启动流程(PC机—Linux系统、嵌入式ARM—linux系统的启动流程类似) 二、编译u-boot2.1 u-bo…...

Qt打开excel文件,并读取指定单元格数据
1. 下载并安装QXlsx库,详见之前的博文Qt子线程创建excel文件报错QObject: Cannot create children for a parent that is in a different thread.-CSDN博客 2. // 创建一个XlsxDocument对象QString filename "D:\\mydocuments\\data_acquisition\\data\\tes…...

适合下班回家做的小副业,用AI做视频,几天时间3000+
大家好,今天要给大家分享的项目是定制儿歌,精准定位宝妈群体,每天轻松赚500! ***01* 项目原理 父母都非常疼爱自己的孩子,愿意为孩子提供独特的东西。而我们正是利用这一点,通过免费AI工具生成专属的儿童…...

git的基本操作 + 分支管理
一、基本操作 1. 修改文件 Git比其他的版本管理器设计得更加优秀,因为Git追踪并管理的是修改,而非文件。 修改一个文件,不管你是添加一行,或者删除一行,还是添加了又删除了,甚至你创建了一个新文件&…...

VRRP
1、VRRP简介 虚拟路由冗余协议 VRRP(Virtual Router Redundancy Protocol)通过把几台路由设备联合组成一台虚拟的路由设备,将虚拟路由设备的IP地址作为用户的默认网关实现与外部网络通信。当网关设备发生故障时(单点故障…...

个人健康系统|个人健康数据管理系统|基于小程序+java的个人健康数据管理系统设计与实现(源码+数据库+文档)
个人健康数据管理系统 目录 基于小程序java的个人健康数据管理系统设计与实现 一、前言 二、系统功能设计 三、系统实现 四、数据库设计 五、核心代码 六、论文参考 七、最新计算机毕设选题推荐 八、源码获取: 博主介绍:✌️大厂码农|毕设布道师…...

R语言统计分析——折线图
参考资料:R语言实战【第2版】 如果将散点图上的点从左到右连接起来,就会得到一个折线图。以基础安装中的Orange数据集为例,展示如下: # 设置绘图参数 opar<-par(no.readonly TRUE) # 画布拆分为1行2列 par(mfrowc(1,2)) # 选…...

前端怎么实现电子签名
电子签名(e-signature)作为一种数字化的签署方式,广泛应用于合同、协议等文件的确认中。随着科技的发展,前端技术也为电子签名的实现提供了便利。本文将探讨在前端如何实现电子签名,包括技术选型、实现步骤及注意事项。…...

数字后端零基础入门系列 | Innovus零基础LAB学习Day1
一 Floorplan 数字IC后端设计如何从零基础快速入门?(内附数字IC后端学习视频) Lab5-1这个lab学习目标很明确——启动Innovus工具并完成设计的导入。 在进入lab之前,我们需要进入我们的FPR工作目录。 其中ic062为个人服务器账户。比如你端…...

鼠标移入盒子,盒子跟随鼠标移动
demo效果: 鼠标移入盒子,按下鼠标,开启移动跟随移动模式,再次按下关闭移动模式 涉及主要属性 在元素上单击鼠标按钮时输出鼠标指针的坐标: var x event.pageX; // 获取水平坐标 var y event.pageY; // 获取垂直坐标元素offsetL…...

css的简单问题
1.display:none;和visibility:hidden;的区别 相同点:都可以让元素不可见 区别: display:none;可以让元素完成在渲染树中消失,渲染时不占任何空间;visibility:hidden;不会让元素从渲染树消失,渲染元素继续占据空间&a…...