当前位置: 首页 > news >正文

[数据结构]带头双向循环链表的实现与应用

文章目录

  • 一、引言
  • 二、链表的基本概念
    • 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、问题背景 问题&#xff…...

python开发环境配置

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

QT开发--QT SQL模块

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

如何保证接口幂等性?

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

【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…...

逻辑回归:给不确定性划界的分类大师

想象你是一名医生。面对患者的检查报告(肿瘤大小、血液指标),你需要做出一个**决定性判断**:恶性还是良性?这种“非黑即白”的抉择,正是**逻辑回归(Logistic Regression)** 的战场&a…...

.Net框架,除了EF还有很多很多......

文章目录 1. 引言2. Dapper2.1 概述与设计原理2.2 核心功能与代码示例基本查询多映射查询存储过程调用 2.3 性能优化原理2.4 适用场景 3. NHibernate3.1 概述与架构设计3.2 映射配置示例Fluent映射XML映射 3.3 查询示例HQL查询Criteria APILINQ提供程序 3.4 高级特性3.5 适用场…...

遍历 Map 类型集合的方法汇总

1 方法一 先用方法 keySet() 获取集合中的所有键。再通过 gey(key) 方法用对应键获取值 import java.util.HashMap; import java.util.Set;public class Test {public static void main(String[] args) {HashMap hashMap new HashMap();hashMap.put("语文",99);has…...

基于Flask实现的医疗保险欺诈识别监测模型

基于Flask实现的医疗保险欺诈识别监测模型 项目截图 项目简介 社会医疗保险是国家通过立法形式强制实施,由雇主和个人按一定比例缴纳保险费,建立社会医疗保险基金,支付雇员医疗费用的一种医疗保险制度, 它是促进社会文明和进步的…...

Leetcode 3577. Count the Number of Computer Unlocking Permutations

Leetcode 3577. Count the Number of Computer Unlocking Permutations 1. 解题思路2. 代码实现 题目链接:3577. Count the Number of Computer Unlocking Permutations 1. 解题思路 这一题其实就是一个脑筋急转弯,要想要能够将所有的电脑解锁&#x…...

在四层代理中还原真实客户端ngx_stream_realip_module

一、模块原理与价值 PROXY Protocol 回溯 第三方负载均衡(如 HAProxy、AWS NLB、阿里 SLB)发起上游连接时,将真实客户端 IP/Port 写入 PROXY Protocol v1/v2 头。Stream 层接收到头部后,ngx_stream_realip_module 从中提取原始信息…...

新能源汽车智慧充电桩管理方案:新能源充电桩散热问题及消防安全监管方案

随着新能源汽车的快速普及,充电桩作为核心配套设施,其安全性与可靠性备受关注。然而,在高温、高负荷运行环境下,充电桩的散热问题与消防安全隐患日益凸显,成为制约行业发展的关键瓶颈。 如何通过智慧化管理手段优化散…...

如何理解 IP 数据报中的 TTL?

目录 前言理解 前言 面试灵魂一问:说说对 IP 数据报中 TTL 的理解?我们都知道,IP 数据报由首部和数据两部分组成,首部又分为两部分:固定部分和可变部分,共占 20 字节,而即将讨论的 TTL 就位于首…...

Maven 概述、安装、配置、仓库、私服详解

目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...

【数据分析】R版IntelliGenes用于生物标志物发现的可解释机器学习

禁止商业或二改转载,仅供自学使用,侵权必究,如需截取部分内容请后台联系作者! 文章目录 介绍流程步骤1. 输入数据2. 特征选择3. 模型训练4. I-Genes 评分计算5. 输出结果 IntelliGenesR 安装包1. 特征选择2. 模型训练和评估3. I-Genes 评分计…...