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

【初阶数据结构】深入解析带头双向循环链表:探索底层逻辑

在这里插入图片描述
🔥引言

本篇将介绍带头双向循环链表底层实现以及在实现中需要注意的事项,帮助各位在使用过程中根据底层实现考虑到效率上问题和使用时可能会导致的错误使用

请添加图片描述
Alt

🌈个人主页:是店小二呀
🌈C语言笔记专栏:C语言笔记
🌈C++笔记专栏: C++笔记
🌈初阶数据结构笔记专栏: 初阶数据结构笔记

🌈喜欢的诗句:无人扶我青云志 我自踏雪至山巅
请添加图片描述

文章目录

  • 一、前文
  • 二、实现带头双向循环链表
    • 2.1 认识头节点
    • 2.2 链表中节点成员
    • 2.3 创建双向循环链表的节点
    • 2.4 双向循环链表的插入节点
      • 2.4.1 双向循环链表的尾插
    • 2.4.2 双向循环链表的头插
    • 2.5 双向循环链表的删除节点
      • 2.5.1 双向循环链表的尾删
      • 2.5.2 双向循环链表的头删
    • 2.6 双向循环链表的查找
    • 2.7 实现双向循环链表任意位置的插入和删除
      • 2.7.1 任意位置插入
      • 2.7.2 任意位置删除
    • 2.8 双向循环链表的打印
  • 三、双向循环链表的好处

一、前文

链表的分类有很多种,只需要将无头单向非循环链表和带头双向循环链表掌握,也就理解了剩下链表构成和实现。带头双向循环链表,结构复杂,一般只用于单独存储数据。但是也由于结构,带来了很多的优势,从而复杂结构,反而简单低实现。

二、实现带头双向循环链表

2.1 认识头节点

头节点(哨兵位)是指链表里面第一个节点,它不存放任何信息或存储任何有效元素,起到"放哨"作用,作用是减少了对一个节点是否为空的判断。

对于之前实现的单链表是不带哨兵位的,但是称第一个节点为头节点是不规范的,但是那样方便学习中称呼。

2.2 链表中节点成员

首先节点的成员:有效带数值,前驱指针,后继指针

  • 前驱指针:以当前节点为参照物,向左就是前驱指针

  • 后继指针:以当前节点为参照物,向右就是后继指针

typedef int LTDataType;//处理不同的数据类型typedef struct SLTlistNode
{LTDataType val;struct SLTlistNode* next;struct SLTlistNode* prev;}SLNode;

2.3 创建双向循环链表的节点

SLNode* CreatNewNode(LTDataType x)
{SLNode* newnode = (SLNode*)malloc(sizeof(SLNode));if (newnode == NULL){perror("malloc fail!");return ;}newnode->next = newnode;newnode->prev = newnode;newnode->val = x;return newnode;
}

这里需要注意的是:跟实现单链表的节点,大体相同。但是需要前驱指针,后继指针都指向自己设计为一个闭环,在插入中这样子的设计有很好的优势

2.4 双向循环链表的插入节点

2.4.1 双向循环链表的尾插

void SLTPushBack(SLNode* head, LTDataType x)
{assert(head);SLNode* newnode = CreatNewNode(x);SLNode* tail = head->prev;//尾插,需要找到尾tail->next = newnode;newnode->prev = tail;head->prev = newnode;newnode->next = head;
}

在这里插入图片描述

这里需要注意的是:由于一开始哨兵位就是循环体,一开始创建指向尾的指针,也是指向自己,这种结构具有很好的优势,维持闭环的状态,在进行插入或删除操作时,提供了便利。当进行尾插时,需要找到尾,再进行尾插操作。

2.4.2 双向循环链表的头插

void SLTPushFront(SLNode* head, LTDataType x)//双向循环链表无空指针
{assert(head);	if (head->next==head){SLTPushBack(head, x);}else{SLNode* newnode = CreatNewNode(x);SLNode* tail = head->prev;head->next = newnode;newnode->prev = head;newnode->next = tail;tail->prev = newnode;}
}

在这里插入图片描述

这里需要注意的是 : 头插是值第一个有效数据节点前面插入,不是在哨兵位前面进行插入。当只有哨兵位存在时,这里跟尾插操作是一样的,剩下跟单链表的任意位置插入差不多,主要是改变指针的指向

2.5 双向循环链表的删除节点

2.5.1 双向循环链表的尾删

void SLTPopBack(SLNode* head)//哨兵位不删
{assert(head);assert(head->next!=head);SLNode* tail = head->prev;SLNode* tailprev = tail->prev;tailprev->next = head;head->prev = tailprev;free(tail);tail = NULL;
}

在这里插入图片描述

这里需要注意的是:哨兵位不参与对节点进行操作时,对此不对哨兵位进行删除操作。由于循环体虽然没有空指针,但是可能会出现野指针现象。可以不置空free(tail),不对tail指向空间进行访问。

2.5.2 双向循环链表的头删

void SLTPopFront(SLNode* head)
{assert(head);SLNode* cur = head->next;SLNode* curnext = cur->next;if (head->next == head){return 1;}else{head->next = curnext;curnext->prev = head;free(cur);cur = NULL;}
}

在这里插入图片描述

这里需要注意的是:哨兵位不参与对节点进行操作时,对此不对哨兵位进行删除操作。双向循环的优势在此体现出来了,只需在curcurnext位置上处理。

2.6 双向循环链表的查找

SLNode* SLFind(SLNode* head, LTDataType x)
{assert(head);SLNode* cur = head->next;while (cur!=head){if (cur->val == x){return cur;}cur = cur->next;}return NULL;
}

这里需要注意的是:当cur==head时,说明cur遍历完了链表。如果没有符合的值,则表示不存在;反之返回该位置的指针。

2.7 实现双向循环链表任意位置的插入和删除

2.7.1 任意位置插入

void SLInsert(SLNode* head, SLNode* pos,LTDataType x)//之前插入
{assert(head);SLNode* posprve = pos->prev;if (head->next == head){SLTPushBack(head,x);}else{SLNode* newnode = CreateLTNode(x);posprve->next = newnode;newnode->prev = posprve;pos->prev = newnode;newnode->next = pos; }
}

这里需要注意的是:当不存在有效数据时,默认是尾插操作。具体实现逻辑,知道posposprev的地址,再通过改变指针完成链接

在这里插入图片描述

2.7.2 任意位置删除

void SLEmpty(SLNode* head, SLNode* pos, LTDataType x)
{assert(pos != head);assert(head);SLNode* posprve = pos->prev;SLNode* frist = posprve->prev;if (cur->next == head){SLTPopBack(head);}else{frist->next = pos;pos->prev = frist;free(posprve);}
}

在这里插入图片描述

这里需要注意的是:哨兵位不参与对节点进行操作时,对此不对哨兵位进行删除操作。只存在一个有效数据时,只进行尾删操作即可。对于三个指针的位置关系,满足pos在两个指针之间

以上就是双向循环链表的核心接口,接下来实现一些实用小接口,丰富我们的双向循环链表

2.8 双向循环链表的打印

void SLTPrint(SLNode* head)
{assert(head);printf("哨兵位<->");SLNode* cur = head->next;while (cur != head ){printf("%d<->", cur->val);cur = cur->next;}
}

##2.9 双向循环链表的销毁

void SLDestroy(SLNode* head)
{assert(head);SLNode* cur = head->next;//结束条件是什么,这里是无死角的-->先销毁哨兵位之外的空间while (cur!=head){SLNode* curnext = cur->next;free(cur);cur = curnext;}free(head);
}

这里需要注意的是:先将除哨兵位之外的空间释放,最后在释放哨兵位

三、双向循环链表的好处

在实现过程中,我们清晰地知道,如果需要快速搭建一个链表,实现双向循环链表中任意插入、删除是很快的,这里利用到了结构特点


请添加图片描述

以上就是本篇文章的所有内容,在此感谢大家的观看!这里是店小二初阶数据结构笔记,希望对你在学习初阶数据结构中有所帮助!

相关文章:

【初阶数据结构】深入解析带头双向循环链表:探索底层逻辑

&#x1f525;引言 本篇将介绍带头双向循环链表底层实现以及在实现中需要注意的事项&#xff0c;帮助各位在使用过程中根据底层实现考虑到效率上问题和使用时可能会导致的错误使用 &#x1f308;个人主页&#xff1a;是店小二呀 &#x1f308;C语言笔记专栏&#xff1a;C语言笔…...

【面试干货】Java中的访问修饰符与访问级别

【面试干货】Java中的访问修饰符与访问级别 1、public2、protected3、默认&#xff08;没有访问修饰符&#xff09;4、private &#x1f496;The Begin&#x1f496;点点关注&#xff0c;收藏不迷路&#x1f496; 在Java中&#xff0c;访问修饰符用于控制类、变量、方法和构造器…...

Oracle最终还是杀死了MySQL

起因 大约15年前&#xff0c;Oracle收购了Sun公司&#xff0c;从而也拥有了MySQL&#xff0c;互联网上关于Oracle何时会“扼杀MySQL”的讨论此起彼伏。 当时流传着各种理论&#xff1a;从彻底扼杀 MySQL 以减少对 Oracle 专有数据库的竞争&#xff0c;到干掉 MySQL 开源项目&…...

【Python的随机数汇总】

​我们写python代码的时候&#xff0c;很少能用得上随机数&#xff0c;但是随机数有很多妙用。例如&#xff0c;在我们做测试数据集的时候&#xff0c;可以构建一个随机的dataframe&#xff1b; 或者在保存数据的时候&#xff0c;可以在每条数据前插入一列作为&#xff0c;不重…...

[状态压缩 广搜BFS]Saving Tang Monk

描述 《Journey to the West》(also 《Monkey》) is one of the Four Great Classical Novels of Chinese literature. It was written by Wu Chengen during the Ming Dynasty. In this novel, Monkey King Sun Wukong, pig Zhu Bajie and Sha Wujing, escorted Tang Monk to…...

Flutter 实现软鼠标

文章目录 前言一、如何实现&#xff1f;1、记录鼠标偏移2、MouseRegion获取偏移3、Transform移动图标 二、完整代码三、使用示例总结 前言 flutter在嵌入式系统中运行时&#xff0c;有可能遇到drm鼠标无法使用的情况&#xff0c;但鼠标事件却可以正常接收&#xff0c;此时如果…...

使用 MLRun 和 MinIO 设置开发机器

MLOps 之于机器学习&#xff0c;就像 DevOps 之于传统软件开发一样。两者都是一组旨在改善工程团队&#xff08;开发或 ML&#xff09;和 IT 运营 &#xff08;Ops&#xff09; 团队之间协作的实践和原则。目标是使用自动化来简化开发生命周期&#xff0c;从规划和开发到部署和…...

资质申请表详解:填写《建筑幕墙工程设计专项资质申请表》的要点

填写《建筑幕墙工程设计专项资质申请表》的要点如下&#xff0c;按照清晰、分点表示和归纳的方式整理&#xff0c;并参考了文章中的相关数字和信息&#xff1a; 一、封面 申报企业名称&#xff1a;按照工商营业执照内容填写全称&#xff0c;并加盖企业公章。填报日期&#xf…...

华为手机怎么找回删除的照片?掌握3个方法,恢复不是梦

由于误删、设备故障、软件更新等原因&#xff0c;我们有时可能会不慎丢失这些宝贵的照片。当面对空空如也的相册时&#xff0c;那种失落感无法言喻。华为手机该怎么找回删除的照片呢&#xff1f;但是&#xff0c;请不要绝望&#xff01;在科技的帮助下&#xff0c;我们可以采取…...

数据结构试题 20-21

真需要就死记吧 二叉树遍历-先序(非递归)【图解代码】_哔哩哔哩_bilibili 解释一下步骤&#xff1a; 一个循环为&#xff1a; 1.取节点 2.放右子树 3.放左子树 每次循环&#xff0c;都要从栈里取出一个节点 先放右子树&#xff0c;再放左子树 那这道题就是&#xff0c;先放1&am…...

vscode插件开发之 - TestController

TesController概要介绍 TestController 组件是用于实现自定义测试框架和集成测试结果的。它允许开发者定义自己的测试运行器&#xff0c;以支持在VSCode中运行和展示测试。以下是一些使用 TestController 组件的主要场景&#xff1a; 自定义测试框架&#xff1a;如果你正在开发…...

QBitArray使用详解

QBitArray使用详解 一、创建和初始化 QBitArray1.1 QBitArray默认构造1.2 QBitArray指定大小的构造1.3 QBitArray指定大小和初始值的构造 二、设置和访问位2.1 QBitArray设置单个位2.2 QBitArray访问单个位2.3 QBitArray使用下标操作符 三、设置所有位3.1 QBitArray将所有位设置…...

基于Python的自然语言处理项目 ChatTTS 推荐

**项目名称&#xff1a;ChatTTS**  ChatTTS是一个基于Python的自然语言处理项目&#xff0c;旨在实现一个简单的文本到语音转换系统。它使用深度学习技术&#xff0c;通过自然语言处理和语音合成算法&#xff0c;将文本转换为语音输出。  **项目介绍**&#xff1a;  Chat…...

论 To B 产品:从概念到市场实践

本文作者为 360 奇舞团产品经理 论 To B 产品&#xff1a;从概念到市场实践 To B 产品在商业世界中扮演着至关重要的角色。相较于面向消费者的To C市场&#xff0c;To B市场更专注于为其他企业提供产品和服务。理解和成功运营To B产品需要对其特定的市场需求和运作方式有深刻的…...

如何通过自定义模块DIY出专属个性化的CSDN主页?一招教你搞定!

个人主页&#xff1a;学习前端的小z 个人专栏&#xff1a;HTML5和CSS3悦读 本专栏旨在分享记录每日学习的前端知识和学习笔记的归纳总结&#xff0c;欢迎大家在评论区交流讨论&#xff01; 文章目录 &#x1f4af;如何通过HTMLCSS自定义模板diy出自己的个性化csdn主页&#x…...

[BSidesCF 2020]Had a bad day1

看到页面有两个按钮 先随便点一个试一下&#xff0c;当我们点击之后发现url是有变动的 感觉url是有点东西的&#xff0c;可能是某种注入&#xff0c;先尝试一下sql注入&#xff0c;发现给出了报错 通过报错我们可以确定是文件包含漏洞&#xff0c;那我们试试php伪协议去读取一下…...

从媒体网站的频道划分看媒体邀约的分类?

传媒如春雨&#xff0c;润物细无声&#xff0c;大家好&#xff0c;我是51媒体网胡老师。 媒体宣传加速季&#xff0c;100万补贴享不停&#xff0c;一手媒体资源&#xff0c;全国100城线下落地执行。详情请联系胡老师。 在我们举行活动的时候&#xff0c;通常会邀请媒体到现场来…...

Day40

Day40 监听器 概念&#xff1a; 监听器用于监听web应用中某些对象信息的创建、销毁、增加&#xff0c;修改&#xff0c;删除等动作的 发生&#xff0c;然后作出相应的响应处理。当范围对象的状态发生变化的时候&#xff0c;服务器自动调用 监听器对象中的方法。 常用于统计在线…...

linux基础 - 内核的基础概念

目录 零. 前言 一. 源码简介 二. 存储管理 物理内存管理&#xff1a; 虚拟内存管理&#xff1a; 内存分配与回收&#xff1a; 三. CPU 和进程管理 进程管理&#xff1a; CPU 管理&#xff1a; 四. 文件系统 文件系统的概念 常见的 Linux 文件系统类型 文件系统的工…...

centos7系统使用docker-compose安装部署jenkins

CentOS7系统使用docker-compose安装部署jenkins&#xff0c;并实现前后端自动构建 记录一次工作中部署jenkins的真实经历&#xff0c;总结了相关经验 1.准备环境 1.java 由于最新的jenkins需要jdk11以上才能支持&#xff0c;而系统里的jdk是1.8的&#xff0c;因此等jenkins安…...

相机Camera日志分析之三十一:高通Camx HAL十种流程基础分析关键字汇总(后续持续更新中)

【关注我,后续持续新增专题博文,谢谢!!!】 上一篇我们讲了:有对最普通的场景进行各个日志注释讲解,但相机场景太多,日志差异也巨大。后面将展示各种场景下的日志。 通过notepad++打开场景下的日志,通过下列分类关键字搜索,即可清晰的分析不同场景的相机运行流程差异…...

管理学院权限管理系统开发总结

文章目录 &#x1f393; 管理学院权限管理系统开发总结 - 现代化Web应用实践之路&#x1f4dd; 项目概述&#x1f3d7;️ 技术架构设计后端技术栈前端技术栈 &#x1f4a1; 核心功能特性1. 用户管理模块2. 权限管理系统3. 统计报表功能4. 用户体验优化 &#x1f5c4;️ 数据库设…...

C++使用 new 来创建动态数组

问题&#xff1a; 不能使用变量定义数组大小 原因&#xff1a; 这是因为数组在内存中是连续存储的&#xff0c;编译器需要在编译阶段就确定数组的大小&#xff0c;以便正确地分配内存空间。如果允许使用变量来定义数组的大小&#xff0c;那么编译器就无法在编译时确定数组的大…...

PHP 8.5 即将发布:管道操作符、强力调试

前不久&#xff0c;PHP宣布了即将在 2025 年 11 月 20 日 正式发布的 PHP 8.5&#xff01;作为 PHP 语言的又一次重要迭代&#xff0c;PHP 8.5 承诺带来一系列旨在提升代码可读性、健壮性以及开发者效率的改进。而更令人兴奋的是&#xff0c;借助强大的本地开发环境 ServBay&am…...

stm32wle5 lpuart DMA数据不接收

配置波特率9600时&#xff0c;需要使用外部低速晶振...

【堆垛策略】设计方法

堆垛策略的设计是积木堆叠系统的核心&#xff0c;直接影响堆叠的稳定性、效率和容错能力。以下是分层次的堆垛策略设计方法&#xff0c;涵盖基础规则、优化算法和容错机制&#xff1a; 1. 基础堆垛规则 (1) 物理稳定性优先 重心原则&#xff1a; 大尺寸/重量积木在下&#xf…...

数据结构:递归的种类(Types of Recursion)

目录 尾递归&#xff08;Tail Recursion&#xff09; 什么是 Loop&#xff08;循环&#xff09;&#xff1f; 复杂度分析 头递归&#xff08;Head Recursion&#xff09; 树形递归&#xff08;Tree Recursion&#xff09; 线性递归&#xff08;Linear Recursion&#xff09;…...

向量几何的二元性:叉乘模长与内积投影的深层联系

在数学与物理的空间世界中&#xff0c;向量运算构成了理解几何结构的基石。叉乘&#xff08;外积&#xff09;与点积&#xff08;内积&#xff09;作为向量代数的两大支柱&#xff0c;表面上呈现出截然不同的几何意义与代数形式&#xff0c;却在深层次上揭示了向量间相互作用的…...

leetcode_69.x的平方根

题目如下 &#xff1a; 看到题 &#xff0c;我们最原始的想法就是暴力解决: for(long long i 0;i<INT_MAX;i){if(i*ix){return i;}else if((i*i>x)&&((i-1)*(i-1)<x)){return i-1;}}我们直接开始遍历&#xff0c;我们是整数的平方根&#xff0c;所以我们分两…...

2.2.2 ASPICE的需求分析

ASPICE的需求分析是汽车软件开发过程中至关重要的一环&#xff0c;它涉及到对需求进行详细分析、验证和确认&#xff0c;以确保软件产品能够满足客户和用户的需求。在ASPICE中&#xff0c;需求分析的关键步骤包括&#xff1a; 需求细化&#xff1a;将从需求收集阶段获得的高层需…...