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

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

目录

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开始的一段数据都加载进去缓存,加载多少取决于硬件

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

 

相关文章:

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

目录 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.带头双向循环…...

问道管理:日换手率达20是好是坏?

关于股票商场的出资者而言&#xff0c;日换手率是一个非常重要的目标。日换手率是指股票当日买卖量与该股总股本之比。假如一只股票的日换手率过高&#xff0c;那么就意味着该股票的流动性较强&#xff0c;而假如日换手率过低&#xff0c;那么就意味着该股票的流动性较弱。 那…...

勃艮第葡萄酒是如何分级的?

勃艮第葡萄酒来自一个同名的地区:勃艮第&#xff0c;它位于法国中东部&#xff0c;在西部的卢瓦尔河和东部的索恩河之间。该地区最大的城市是欧塞尔、第戎、马孔和内韦尔。由于地处国家中心&#xff0c;勃艮第属于大陆性气候&#xff0c;夏季炎热&#xff0c;冬季寒冷。这种气候…...

使用awvs进行web安全扫描

1、安装 docker pull secfa/docker-awvs docker run -it -d -name awvs -p 13443:3443 --cap-add LINUX_IMMUTABLE secfa/docker-awvs2、账号密码 # https://ip:13443/ # 用户名:adminadmin.com # 密码:Admin1233、使用 ps:需要征得甲方的同意...

抖音小程序开发教学系列(1)- 抖音小程序简介

章节一&#xff1a;抖音小程序简介 1.1 抖音小程序的背景和概述 抖音小程序的发展背景和市场趋势&#xff1a; 抖音作为一款热门的短视频社交平台&#xff0c;用户群体庞大&#xff0c;社交共享的特性也为小程序的发展提供了广阔的空间。抖音小程序作为抖音在社交和用户粘性…...

【4.Vue兄弟组件之间传值-Bus总线】

1.概述 通过创建一个新的vm对象,专门统一注册事件,供所有组件共同操作,达到所有组件随意隔代传值的效果 也就是:各个组件内部要传输的数据或者要执行的命令信息,靠bus来通信。 2. 代码实现 2.1 全局引入 全局引入的话,就直接在main.js里面引入即可: // 创建 bus总线 V…...

element中Notification组件(this.$notify)自定义样式

1、自定义样式效果 2、vue代码 this.notifications this.$notify({title: ,dangerouslyUseHTMLString: true,duration: obj.remindMethod3 ? 0:4500,customClass: notify-warning,offset: 50,showClose: false,message: this.$createElement("div",null,[this.$…...

Manjaro安装使用

Manjaro安装使用 1.先更改镜像源&#xff1a;sudo pacman-mirrors -c China -g 2.安装第三方软件管理工具 &#xff1a;sudo pacman -Sy yay 导入GPG Key sudo pacman -Syy && sudo pacman -S archlinuxcn-keyring安装输入法 sudo pacman -S fcitx-im (#默认全部安装…...

【iOS】折叠cell

文章目录 前言一、实现效果二、折叠cell的实现原理三、实现折叠cell的高度变化四、实现选中点击的单元格总结 前言 在暑假的3GShare中用到了折叠cell控件&#xff0c;特此总结博客记录 一、实现效果 二、折叠cell的实现原理 首先我们需要知道ScrollView的是TableView的父类&a…...

无涯教程-Android - DatePicker函数

Android Date Picker允许您在自定义用户界面中选择由日,月和年组成的日期。为此功能,android提供了DatePicker和DatePickerDialog组件。 在本教程中,我们将通过DatePickerDialog演示日期选择器的用法, DatePickerDialog是一个包含DatePicker的简单对话框。 为了显示DatePicker…...

经纬恒润荣获吉利汽车“最佳价值贡献”奖

8月18日&#xff0c;以“全面向新 共创共赢”为主题&#xff0c;吉利汽车在宁波成功举行2023年电子电器核心供应商恳谈会。经纬恒润凭借在项目合作上持续创新、高效协同等优异表现&#xff0c;获得“最佳价值贡献”奖项。 作为国产汽车代表性品牌之一&#xff0c;吉利汽车积极推…...

【多线程】lock与synchronized的区别

相同点&#xff1a; 1、他们都是Java中用于解决线程安全的工具&#xff0c;两者的性能相差不大 不同点&#xff1a; 1、在实现上synchronized引入了偏向锁、轻量级锁、重量级锁、锁升级来优化加锁的性能&#xff0c;而lock则使用自旋锁来实现性能的优化 2、synchronized是J…...

什么是RTC

参考&#xff1a; https://zhuanlan.zhihu.com/p/377100294 RTC&#xff08;Real time communication&#xff09;实时通信&#xff0c;是实时音视频的一个简称&#xff0c;我们常说的RTC技术一般指的是WebRTC技术&#xff0c;已经被 W3C 和 IETF 发布为正式标准。由于几乎所…...

BW 源/目标模型主键不一样,增量的作用

最近项目上&#xff0c;做了一个复杂的需求逻辑&#xff0c;源模型到目标模型&#xff0c;主键完全发生了变化。用转换的传统功能&#xff0c;我担心处理起来不方便就使用了专家历程&#xff08;这个说明在之前有说过&#xff09;。 项目上线后&#xff0c;发生很多异常事件。但…...

HK1 RBOX X4,Vontar X4,S905 X4 刷 ATV

准备工作 需要HK1 RBOX X4一个&#xff08;内存版本不限 通刷&#xff09;&#xff0c;机顶盒电源&#xff0c;USB双公线一条&#xff08;可以使用两个usb数据线剪开后相同颜色对接使用&#xff0c;最好使用电烙铁焊接一下更稳定&#xff09;&#xff0c;安装 INTEL CPU 运行 w…...

Rust 学习笔记(持续更新中…)

一、 编译和运行是单独的两步 运行 Rust 程序之前必须先编译&#xff0c;命令为&#xff1a;rustc 源文件名 - rustc main.rs编译成功之后&#xff0c;会生成一个二进制文件 - 在 Windows 上还会生产一个 .pdb 文件 &#xff0c;里面包含调试信息Rust 是 ahead-of-time 编译的…...

递归算法学习——电话号码的字母组成,括号生成,组合

目录 一&#xff0c;电话号码的字母组合 1.题意 2.例子 3.题目接口 4.解题代码和思路 代码&#xff1a; 思路&#xff1a; 二&#xff0c;括号的生成 1.题意 2.例子 3.题目接口 四&#xff0c;解题代码和思路 1.先写代码&#xff1a; 2.思路 三&#xff0c;组合 …...

记录 JSONObject.parseObject json对象转换 对象字段为null

1.业务背景 使用websocket 接收消息都是String类型&#xff0c;没办法自定义实体类接收&#xff0c;所以接发都必须将json 转 对象 对象转 json。 这是我最开始的实体类&#xff0c;也就是转换的类型 package com.trinity.system.domain;import lombok.AllArgsConstructor; im…...

Android Native Code开发学习(二)JNI互相传参返回调用

Android Native Code开发学习&#xff08;二&#xff09; 本教程为native code学习笔记&#xff0c;希望能够帮到有需要的人 我的电脑系统为ubuntu 22.04&#xff0c;当然windows也是可以的&#xff0c;区别不大 一、native code介绍 native code就是在android项目中混合C或…...

Ubuntu 下安装Qt5.12.12无法输入中文解决方法

Ubuntu 下安装Qt5.12.12无法输入中文解决方法 一&#xff0c;环境&#xff1a; &#xff08;1&#xff09;VMware Workstation 15 Pro &#xff08;2&#xff09;Ubuntu 20.04 &#xff08;3&#xff09;Qt 5.12.12 64bits &#xff08;4&#xff09;Qt Creator 5.0.2 &#…...

Python爬虫实战:研究MechanicalSoup库相关技术

一、MechanicalSoup 库概述 1.1 库简介 MechanicalSoup 是一个 Python 库,专为自动化交互网站而设计。它结合了 requests 的 HTTP 请求能力和 BeautifulSoup 的 HTML 解析能力,提供了直观的 API,让我们可以像人类用户一样浏览网页、填写表单和提交请求。 1.2 主要功能特点…...

【网络安全产品大调研系列】2. 体验漏洞扫描

前言 2023 年漏洞扫描服务市场规模预计为 3.06&#xff08;十亿美元&#xff09;。漏洞扫描服务市场行业预计将从 2024 年的 3.48&#xff08;十亿美元&#xff09;增长到 2032 年的 9.54&#xff08;十亿美元&#xff09;。预测期内漏洞扫描服务市场 CAGR&#xff08;增长率&…...

家政维修平台实战20:权限设计

目录 1 获取工人信息2 搭建工人入口3 权限判断总结 目前我们已经搭建好了基础的用户体系&#xff0c;主要是分成几个表&#xff0c;用户表我们是记录用户的基础信息&#xff0c;包括手机、昵称、头像。而工人和员工各有各的表。那么就有一个问题&#xff0c;不同的角色&#xf…...

【ROS】Nav2源码之nav2_behavior_tree-行为树节点列表

1、行为树节点分类 在 Nav2(Navigation2)的行为树框架中,行为树节点插件按照功能分为 Action(动作节点)、Condition(条件节点)、Control(控制节点) 和 Decorator(装饰节点) 四类。 1.1 动作节点 Action 执行具体的机器人操作或任务,直接与硬件、传感器或外部系统…...

使用 Streamlit 构建支持主流大模型与 Ollama 的轻量级统一平台

🎯 使用 Streamlit 构建支持主流大模型与 Ollama 的轻量级统一平台 📌 项目背景 随着大语言模型(LLM)的广泛应用,开发者常面临多个挑战: 各大模型(OpenAI、Claude、Gemini、Ollama)接口风格不统一;缺乏一个统一平台进行模型调用与测试;本地模型 Ollama 的集成与前…...

华硕a豆14 Air香氛版,美学与科技的馨香融合

在快节奏的现代生活中&#xff0c;我们渴望一个能激发创想、愉悦感官的工作与生活伙伴&#xff0c;它不仅是冰冷的科技工具&#xff0c;更能触动我们内心深处的细腻情感。正是在这样的期许下&#xff0c;华硕a豆14 Air香氛版翩然而至&#xff0c;它以一种前所未有的方式&#x…...

初探Service服务发现机制

1.Service简介 Service是将运行在一组Pod上的应用程序发布为网络服务的抽象方法。 主要功能&#xff1a;服务发现和负载均衡。 Service类型的包括ClusterIP类型、NodePort类型、LoadBalancer类型、ExternalName类型 2.Endpoints简介 Endpoints是一种Kubernetes资源&#xf…...

【分享】推荐一些办公小工具

1、PDF 在线转换 https://smallpdf.com/cn/pdf-tools 推荐理由&#xff1a;大部分的转换软件需要收费&#xff0c;要么功能不齐全&#xff0c;而开会员又用不了几次浪费钱&#xff0c;借用别人的又不安全。 这个网站它不需要登录或下载安装。而且提供的免费功能就能满足日常…...

Java编程之桥接模式

定义 桥接模式&#xff08;Bridge Pattern&#xff09;属于结构型设计模式&#xff0c;它的核心意图是将抽象部分与实现部分分离&#xff0c;使它们可以独立地变化。这种模式通过组合关系来替代继承关系&#xff0c;从而降低了抽象和实现这两个可变维度之间的耦合度。 用例子…...

Python Einops库:深度学习中的张量操作革命

Einops&#xff08;爱因斯坦操作库&#xff09;就像给张量操作戴上了一副"语义眼镜"——让你用人类能理解的方式告诉计算机如何操作多维数组。这个基于爱因斯坦求和约定的库&#xff0c;用类似自然语言的表达式替代了晦涩的API调用&#xff0c;彻底改变了深度学习工程…...