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

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


🌈个人主页:是店小二呀
🌈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;}
}

这里需要注意的是:哨兵位不参与对节点进行操作时,对此不对哨兵位进行删除操作。双向循环的优势在此体现出来了,只需在cur和curnext位置上处理。
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; }
}
这里需要注意的是:当不存在有效数据时,默认是尾插操作。具体实现逻辑,知道pos和posprev的地址,再通过改变指针完成链接

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);
}
这里需要注意的是:先将除哨兵位之外的空间释放,最后在释放哨兵位
三、双向循环链表的好处
在实现过程中,我们清晰地知道,如果需要快速搭建一个链表,实现双向循环链表中任意插入、删除是很快的,这里利用到了结构特点。

以上就是本篇文章的所有内容,在此感谢大家的观看!这里是店小二初阶数据结构笔记,希望对你在学习初阶数据结构中有所帮助!
相关文章:
【初阶数据结构】深入解析带头双向循环链表:探索底层逻辑
🔥引言 本篇将介绍带头双向循环链表底层实现以及在实现中需要注意的事项,帮助各位在使用过程中根据底层实现考虑到效率上问题和使用时可能会导致的错误使用 🌈个人主页:是店小二呀 🌈C语言笔记专栏:C语言笔…...
【面试干货】Java中的访问修饰符与访问级别
【面试干货】Java中的访问修饰符与访问级别 1、public2、protected3、默认(没有访问修饰符)4、private 💖The Begin💖点点关注,收藏不迷路💖 在Java中,访问修饰符用于控制类、变量、方法和构造器…...
Oracle最终还是杀死了MySQL
起因 大约15年前,Oracle收购了Sun公司,从而也拥有了MySQL,互联网上关于Oracle何时会“扼杀MySQL”的讨论此起彼伏。 当时流传着各种理论:从彻底扼杀 MySQL 以减少对 Oracle 专有数据库的竞争,到干掉 MySQL 开源项目&…...
【Python的随机数汇总】
我们写python代码的时候,很少能用得上随机数,但是随机数有很多妙用。例如,在我们做测试数据集的时候,可以构建一个随机的dataframe; 或者在保存数据的时候,可以在每条数据前插入一列作为,不重…...
[状态压缩 广搜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 实现软鼠标
文章目录 前言一、如何实现?1、记录鼠标偏移2、MouseRegion获取偏移3、Transform移动图标 二、完整代码三、使用示例总结 前言 flutter在嵌入式系统中运行时,有可能遇到drm鼠标无法使用的情况,但鼠标事件却可以正常接收,此时如果…...
使用 MLRun 和 MinIO 设置开发机器
MLOps 之于机器学习,就像 DevOps 之于传统软件开发一样。两者都是一组旨在改善工程团队(开发或 ML)和 IT 运营 (Ops) 团队之间协作的实践和原则。目标是使用自动化来简化开发生命周期,从规划和开发到部署和…...
资质申请表详解:填写《建筑幕墙工程设计专项资质申请表》的要点
填写《建筑幕墙工程设计专项资质申请表》的要点如下,按照清晰、分点表示和归纳的方式整理,并参考了文章中的相关数字和信息: 一、封面 申报企业名称:按照工商营业执照内容填写全称,并加盖企业公章。填报日期…...
华为手机怎么找回删除的照片?掌握3个方法,恢复不是梦
由于误删、设备故障、软件更新等原因,我们有时可能会不慎丢失这些宝贵的照片。当面对空空如也的相册时,那种失落感无法言喻。华为手机该怎么找回删除的照片呢?但是,请不要绝望!在科技的帮助下,我们可以采取…...
数据结构试题 20-21
真需要就死记吧 二叉树遍历-先序(非递归)【图解代码】_哔哩哔哩_bilibili 解释一下步骤: 一个循环为: 1.取节点 2.放右子树 3.放左子树 每次循环,都要从栈里取出一个节点 先放右子树,再放左子树 那这道题就是,先放1&am…...
vscode插件开发之 - TestController
TesController概要介绍 TestController 组件是用于实现自定义测试框架和集成测试结果的。它允许开发者定义自己的测试运行器,以支持在VSCode中运行和展示测试。以下是一些使用 TestController 组件的主要场景: 自定义测试框架:如果你正在开发…...
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 推荐
**项目名称:ChatTTS** ChatTTS是一个基于Python的自然语言处理项目,旨在实现一个简单的文本到语音转换系统。它使用深度学习技术,通过自然语言处理和语音合成算法,将文本转换为语音输出。 **项目介绍**: Chat…...
论 To B 产品:从概念到市场实践
本文作者为 360 奇舞团产品经理 论 To B 产品:从概念到市场实践 To B 产品在商业世界中扮演着至关重要的角色。相较于面向消费者的To C市场,To B市场更专注于为其他企业提供产品和服务。理解和成功运营To B产品需要对其特定的市场需求和运作方式有深刻的…...
如何通过自定义模块DIY出专属个性化的CSDN主页?一招教你搞定!
个人主页:学习前端的小z 个人专栏:HTML5和CSS3悦读 本专栏旨在分享记录每日学习的前端知识和学习笔记的归纳总结,欢迎大家在评论区交流讨论! 文章目录 💯如何通过HTMLCSS自定义模板diy出自己的个性化csdn主页&#x…...
[BSidesCF 2020]Had a bad day1
看到页面有两个按钮 先随便点一个试一下,当我们点击之后发现url是有变动的 感觉url是有点东西的,可能是某种注入,先尝试一下sql注入,发现给出了报错 通过报错我们可以确定是文件包含漏洞,那我们试试php伪协议去读取一下…...
从媒体网站的频道划分看媒体邀约的分类?
传媒如春雨,润物细无声,大家好,我是51媒体网胡老师。 媒体宣传加速季,100万补贴享不停,一手媒体资源,全国100城线下落地执行。详情请联系胡老师。 在我们举行活动的时候,通常会邀请媒体到现场来…...
Day40
Day40 监听器 概念: 监听器用于监听web应用中某些对象信息的创建、销毁、增加,修改,删除等动作的 发生,然后作出相应的响应处理。当范围对象的状态发生变化的时候,服务器自动调用 监听器对象中的方法。 常用于统计在线…...
linux基础 - 内核的基础概念
目录 零. 前言 一. 源码简介 二. 存储管理 物理内存管理: 虚拟内存管理: 内存分配与回收: 三. CPU 和进程管理 进程管理: CPU 管理: 四. 文件系统 文件系统的概念 常见的 Linux 文件系统类型 文件系统的工…...
centos7系统使用docker-compose安装部署jenkins
CentOS7系统使用docker-compose安装部署jenkins,并实现前后端自动构建 记录一次工作中部署jenkins的真实经历,总结了相关经验 1.准备环境 1.java 由于最新的jenkins需要jdk11以上才能支持,而系统里的jdk是1.8的,因此等jenkins安…...
Linux应用开发之网络套接字编程(实例篇)
服务端与客户端单连接 服务端代码 #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <pthread.h> …...
生成xcframework
打包 XCFramework 的方法 XCFramework 是苹果推出的一种多平台二进制分发格式,可以包含多个架构和平台的代码。打包 XCFramework 通常用于分发库或框架。 使用 Xcode 命令行工具打包 通过 xcodebuild 命令可以打包 XCFramework。确保项目已经配置好需要支持的平台…...
tree 树组件大数据卡顿问题优化
问题背景 项目中有用到树组件用来做文件目录,但是由于这个树组件的节点越来越多,导致页面在滚动这个树组件的时候浏览器就很容易卡死。这种问题基本上都是因为dom节点太多,导致的浏览器卡顿,这里很明显就需要用到虚拟列表的技术&…...
rnn判断string中第一次出现a的下标
# coding:utf8 import torch import torch.nn as nn import numpy as np import random import json""" 基于pytorch的网络编写 实现一个RNN网络完成多分类任务 判断字符 a 第一次出现在字符串中的位置 """class TorchModel(nn.Module):def __in…...
Redis的发布订阅模式与专业的 MQ(如 Kafka, RabbitMQ)相比,优缺点是什么?适用于哪些场景?
Redis 的发布订阅(Pub/Sub)模式与专业的 MQ(Message Queue)如 Kafka、RabbitMQ 进行比较,核心的权衡点在于:简单与速度 vs. 可靠与功能。 下面我们详细展开对比。 Redis Pub/Sub 的核心特点 它是一个发后…...
#Uniapp篇:chrome调试unapp适配
chrome调试设备----使用Android模拟机开发调试移动端页面 Chrome://inspect/#devices MuMu模拟器Edge浏览器:Android原生APP嵌入的H5页面元素定位 chrome://inspect/#devices uniapp单位适配 根路径下 postcss.config.js 需要装这些插件 “postcss”: “^8.5.…...
【C++】纯虚函数类外可以写实现吗?
1. 答案 先说答案,可以。 2.代码测试 .h头文件 #include <iostream> #include <string>// 抽象基类 class AbstractBase { public:AbstractBase() default;virtual ~AbstractBase() default; // 默认析构函数public:virtual int PureVirtualFunct…...
springboot 日志类切面,接口成功记录日志,失败不记录
springboot 日志类切面,接口成功记录日志,失败不记录 自定义一个注解方法 import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target;/***…...
Docker拉取MySQL后数据库连接失败的解决方案
在使用Docker部署MySQL时,拉取并启动容器后,有时可能会遇到数据库连接失败的问题。这种问题可能由多种原因导致,包括配置错误、网络设置问题、权限问题等。本文将分析可能的原因,并提供解决方案。 一、确认MySQL容器的运行状态 …...
【Post-process】【VBA】ETABS VBA FrameObj.GetNameList and write to EXCEL
ETABS API实战:导出框架元素数据到Excel 在结构工程师的日常工作中,经常需要从ETABS模型中提取框架元素信息进行后续分析。手动复制粘贴不仅耗时,还容易出错。今天我们来用简单的VBA代码实现自动化导出。 🎯 我们要实现什么? 一键点击,就能将ETABS中所有框架元素的基…...
