c++ deque 的使用

 

目录

1. deque 的介绍

2. deque 底层原理

3. deque 的迭代器 

4. deque 的接口使用 

5. deque 和 vector,list 的比较


1. deque 的介绍

下面是 deque 的介绍,来自于:deque - C++ Reference (cplusplus.com)  的翻译,您可以不用看,我稍后会对 deque 的一种实现做讲解,以便您能更好的理解 deque 。deque 在编程中并不如 vector 和 list 使用得频繁。模拟实现 deque,这是意义不大的行为。

1:deque(通常读作 "deck")是双端队列的不规则缩写。双端队列是具有动态大小的序列容器,可以在两端(前端或后端)扩展或收缩。

2:特定的库可能会以不同的方式实现 deque,通常是作为某种形式的动态数组。但无论如何,它们都允许通过随机访问迭代器直接访问单个元素,并根据需要通过扩展和收缩容器自动处理存储。

因此,它们提供了与 vector 类似的功能,但也能在序列的开头(而不仅仅是结尾)有效地插入和删除元素。但与 vector 不同的是,deque 不能保证将所有元素都存储在连续的存储位置:通过偏移指向另一个元素的指针来访问 deque 中的元素会导致未定义的行为。

3:vector 和 deque 提供了非常相似的接口,可用于类似的目的,但两者内部的工作方式却截然不同: vector 使用的是单个数组,偶尔需要重新分配以适应增长;而 deque 的元素可以分散在不同的存储块中,容器内部保存必要的信息,可以在恒定时间内直接访问其中的任何元素,并提供统一的顺序接口(通过迭代器)。

因此,deque 的内部结构要比 vector 复杂一些,但在某些情况下,deque 可以更高效地增长,尤其是在处理超长序列时,重新分配的代价会更高。

4:与 list 和 forward_list 相比,对于频繁插入或移除位于开头或结尾以外位置的元素的操作,deques 的性能更差,迭代器和引用的一致性也更差。

2. deque 底层原理

deque 中文名,双端队列。但并不是一个队列,因为队列要求先进先出,而 deque 可以头插,头删,尾插,尾删,不符合队列的特征!

下面将会讲解 deque 的底层原理:

1:deque 维护了若干个 buff 数组,用于存储数据。除此之外,deque 还维护了一个中控数组 (指针数组),数组中的元素指向 deque 维护的 buff 数组。

2:中控数组的使用时从中间开始,当中控数组满了之后会直接扩容,然后释放原来的空间。

3:头插,头删数据。

下图中橙色的部分表示已经右数据啦!下面演示依次头插数据:1,2。

 头删数据就是删除 2 啦,如果再次头删,就是删除 1 啦!

4. 尾插,尾删数据。

下图中橙色的部分表示已经右数据啦!下面演示依次尾插数据:1,2。

 依次尾删数据:先删除 2 再删除 1。

5:deque 是支持下标随机访问的,在这看似不连续的空间上,是如何做到的呢?

关键就是每个 buff 的大小必须一样!下面我们来看看是如何做到下标随机访问的!

橙色部分代表有数据。

1):我们要访问下标为 i 的位置,首先要判断他在不在第一个 buff 里面,如果在第一个 buff 里面,那么就可以通过这个 buff 的指针直接访问了。

2):如果下标为 i 的位置不在第一个 buff 里面,那就让 i -= 第一个 buff 数据的个数。然后通过数学运算:

i /= buff.size()

i %= buff.size()

找到下标为 i 的元素在第几个 buff 数组,在这个 buff 数组的第几个位置。

然后通过中控数组,找到这个 buff 访问对应位置上的数据即可。

插入,删除,operator[] 的逻辑倒是讲通了!但是具体怎么实现的呢!deque 的迭代器发挥了至关重要的作用。

3. deque 的迭代器 

deque 的迭代器封转了四个指针:

1:cur:指向一个 buff 中的某个位置,表示该位置的迭代器。

2:first:指向一个 buff 数组的第一个位置,是第一个位置,不是第一个有效元素。

3:last:指向一个 buff 数组的最后一个位置的下一个位置,不是最后一个有效元素的下一个位置。

4:node:指向 buff 在中控数组的位置。

我们来看 deque 是如何通过迭代器遍历元素的:

我们观察 deque 的源代码,发现在 deque 的实现是维护了两个迭代器的,start,finish。分别表示deque 中 front (deque 的第一个元素) 位置对应的迭代器,和 back (deque 的最后一个元素) 位置对应的迭代器。

1:初始化 deque 的迭代器 it = begin(),begin() 就是 start 。

2:it++,这里的加加实际上是让迭代器封装的四个指针指针中的 cur++。

如果 it++ 之后不等于 last,那么直接加加即可。

如果 it++ 之后等于 last,那么我们需要通过 node 找到当前的 buff 数组在中控数组中的位置,让 node++,指向下一个 buff 数组。同时修改 first 和 last 的值,让 cur 指向新的 first。

3:与 end() 的比较,就是当前迭代器的 cur指针 和 deque 内部维护的迭代器 finish 的 cur指针 比较就行啦!

按照上述过程就能实现迭代器遍历整个 deque 。

4. deque 的接口使用 

deque 的接口也是很简单哇!STL 库中的代码风格都差不多,经常使用的接口我在代码中已经贴出来了!

#include<iostream>
#include<queue>
using namespace std;

int main()
{
	deque<int> d;

	d.push_back(5);
	d.push_back(6);
	d.push_back(7);
	d.push_back(8);

	//输出:5 6 7 8
	for (auto e : d)
		cout << e << " ";
	cout << endl;


	d.push_front(4);
	d.push_front(3);
	d.push_front(2);
	d.push_front(1);


	//输出:1 2 3 4 5 6 7 8
	for (auto e : d)
		cout << e << " ";
	cout << endl;

	d.pop_front();
	d.pop_back();

	//输出:2 3 4 5 6 7
	for (int i = 0; i < d.size(); i++)
		cout << d[i] << " ";
	cout << endl;


	cout << d.size() << endl; //输出:6

	cout << d.front() << endl; //输出:2

	cout << d.back() << endl; //输出:7

	cout << d.empty() << endl; //输出:0


	return 0;
}

5. deque 和 vector,list 的比较

deque 被发明出来,本来不仅想解决 vector 头插,头删,扩容拷贝数据带来的效率问题的,还想解决 list 频繁申请释放节点,cpu 高速缓存效率低的问题的!但发现 deque 谁都代替不了。

与vector比较,deque的优势是:头部插入和删除时,不需要搬移元素,效率特别高,而且在扩容时,也不 需要搬移大量的元素,因此其效率是必vector高的。

与list比较,其底层是连续空间,空间利用率比较高,不需要存储额外字段

但是,deque有致命缺陷不适合遍历,因为在遍历时,deque的迭代器要频繁的去检测其是否移动到某段小空间的边界,导致效率低下,而序列式场景中,可能需要经常遍历,因此在实际中,需要线性结构 时,大多数情况下优先考虑 vector 和 list。

还有一个问题就是:deque 想要在中间插入删除,那可老麻烦了,正是因为 每个 buff 数组 的大小相同,才使得 operator[] 比较高效,因此,deque 的插入删除是不能改变 buff 数组的大小的。这不就理了个大谱了嘛。

最后的问题就是:deque 的 operator[] 需要通过计算,在 频繁使用 operator[] 的时候,效率也会变低

deque的应用并不多,而目前能看到的一个应用就是,STL用其作为stack 和 queue 的底层数据结构。

好啦,我们模拟实现 stack 和 queue 的时候再见!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/106498.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【C++初阶】类和对象——构造函数析构函数拷贝构造函数

个人主页点击直达&#xff1a;小白不是程序媛 C系列专栏&#xff1a;C头疼记 目录 前言 类的6个默认成员函数 构造函数 概念 构造函数的特性 析构函数 概念 析构函数特性 拷贝构造函数 概念 拷贝构造函数特性 总结 前言 上篇文章我们对于C中的类有了初步的认识和…

QGIS 捕捉

QGIS 捕捉 0 前言1 捕捉工具的用法 0 前言 在进行矢量编辑的时候&#xff0c;需要直接编辑各个折点&#xff0c;为了方便我们的鼠标能顺利选中我们需要的节点&#xff0c;需要启动捕捉工具。 而捕捉工具的单位为像素&#xff0c;这个可以理解为捕捉工具的灵敏度。捕捉也可以被…

【C++初阶】类与对象(一)

目录 1、初识面向对象思想2、类 struct2.1 C中的struct及使用 3、类 class3.1 类的定义3.2 类的访问限定符3.2.1 访问限定符是什么3.2.2 访问限定符的使用3.2.3 访问限定符的使用规范3.2.4 访问限定符与封装 3.3 类做声明和定义分离3.3.1 声明和定义分离3.3.2 在函数声明的地方…

升级 Xcode 15模拟器 iOS 17.0 Simulator(21A328) 下载失败

升级 IDE Xcode 15 后本地模拟器 Simulator 全被清空,反复重新尝试 Get 下载频频因网络异常断开而导致失败 ... 注:通过 Get 方式下载一定要保证当前网络环境足够平稳,网络环境不好的情况下该方法几乎成不了 解决办法 Get 方式行不通可以尝试通过 官网 途径先下载 模拟器安装包…

移动端之Unity嵌入Android项目开发

目录 前言1 搭建开发环境2 创建Unity项目 2.1 新建项目2.2 Unity构建配置2.3 Android环境相关配置2.4 导出Unity库文件3 创建Android项目 3.1 新建Android项目3.2 Android环境相关配置3.2 导入Unity相关的库3.3 Android中跳转到Unity视图4 进阶扩展 4.1 包体积优化 4.1.1 mono…

HarmonyOS第一课运行Hello World

前言 俗话说&#xff0c;工欲善其事必先利其器。鸿蒙第一课&#xff0c;我们先从简单的Hello World运行说起。要先运行Hello World&#xff0c;那么我们必须搭建HarmonyOS的开发环境。 下载与安装DevEco Studio 在HarmonyOS应用开发学习之前&#xff0c;需要进行一些准备工作&a…

<蓝桥杯软件赛>零基础备赛20周--第2周

报名明年4月蓝桥杯软件赛的同学们&#xff0c;如果你是大一零基础&#xff0c;目前懵懂中&#xff0c;不知该怎么办&#xff0c;可以看看本博客系列&#xff1a;备赛20周合集 20周的完整安排请点击&#xff1a;20周计划 每周发1个博客&#xff0c;共20周&#xff08;读者可以按…

【pwn入门】使用python打二进制

声明 本文是B站你想有多PWN学习的笔记&#xff0c;包含一些视频外的扩展知识。 程序网络交互初体验 将程序部署成可以远程访问的 socat tcp-l:8877,fork exec:./question_1_plus_x64,reuseaddr通过网络访问程序 nc 127.0.0.1 8877攻击脚本 import socket import telnetli…

带你深入了解队列(c/cpp双版本模拟实现)

目录 一.队列的概念及结构 二.队列的实现 2.1队列的结构 2.2初始化队列 2.3队尾入队列 2.4队头出队列 2.5获取队列头部元素 2.6获取队列队尾元素 2.7获取队列中有效元素个数 2.8检测队列是否为空 2.9销毁队列 三.C 版本模拟实现队列 一.队列的概念及结构 队列…

基于 C# 实现样式与数据分离的打印方案

对于八月份的印象&#xff0c;我发现大部分都留给了出差。而九月初出差回来&#xff0c;我便立马投入了新项目的研发工作。因此&#xff0c;无论是中秋节还是国庆节&#xff0c;在这一连串忙碌的日子里&#xff0c;无不充满着仓促的气息。王北洛说&#xff0c;“活着不就是仓促…

Android Glide限定onlyRetrieveFromCache取内存缓存submit超时阻塞方式,Kotlin

Android Glide限定onlyRetrieveFromCache取内存缓存submit超时阻塞方式,Kotlin import android.os.Bundle import android.util.Log import android.widget.ImageView import androidx.appcompat.app.AppCompatActivity import androidx.lifecycle.lifecycleScope import com.b…

剑指JUC原理-4.共享资源和线程安全性

共享问题 小故事 老王&#xff08;操作系统&#xff09;有一个功能强大的算盘&#xff08;CPU&#xff09;&#xff0c;现在想把它租出去&#xff0c;赚一点外快 小南、小女&#xff08;线程&#xff09;来使用这个算盘来进行一些计算&#xff0c;并按照时间给老王支付费用 …

强化学习------PPO算法

目录 简介一、PPO原理1、由On-policy 转化为Off-policy2、Importance Sampling&#xff08;重要性采样&#xff09;3、off-policy下的梯度公式推导 二、PPO算法两种形式1、PPO-Penalty2、PPO-Clip 三、PPO算法实战四、参考 简介 PPO 算法之所以被提出&#xff0c;根本原因在于…

按照正规的软件开发流程,项目原型评审是全程对着页面评审吗

项目原型评审是软件开发过程中的一步&#xff0c;它的目的是确保设计和需求的一致性&#xff0c;以及提供一个可视化的界面供所有相关方进行沟通和理解。评审过程中&#xff0c;可能会涉及到多个方面&#xff1a; 用户界面&#xff08;UI&#xff09;&#xff1a;确保UI设计满足…

电脑如何激活windows

当我们电脑出现如下图&#xff1a; 显示需要激活windows时&#xff0c;操作如下。 1、桌面-新建-文本文档 2、将文档命名为&#xff08;激活windows.bat&#xff09;把原有文档中的后缀.txt去掉 3、点击右键&#xff0c;选择编辑输入代码 slmgr/skms kms.03k.org slmgr/ato4、…

Python----break关键字对while...else结构的影响

案例&#xff1a; 女朋友生气&#xff0c;要求道歉5遍&#xff1a;老婆大人&#xff0c;我错了。道歉到第三遍的时候&#xff0c;媳妇埋怨这一遍说的不真诚&#xff0c;是不是就是要退出循环了&#xff1f;这个退出有两种可能性&#xff1a; ① 更生气&#xff0c;不打算原谅…

c语言进制的转换8进制转换2进制

c语言进制的转换之8进制转换2进制与2转8 c语言的进制的转换 c语言进制的转换之8进制转换2进制与2转8一、八四二一法则二、八进制转换二进制方法三、八进制程序打印 一、八四二一法则 二、八进制转换二进制方法 如&#xff1a;3703转换为2进制 按照八四二一法则&#xff0c;分为…

创纪录的1亿RPS DDoS攻击利用HTTP/2快速重置漏洞

导语&#xff1a;最近&#xff0c;一项创纪录的DDoS攻击引起了广泛关注。攻击者利用了HTTP/2协议中的一个快速重置漏洞&#xff0c;发起了一系列超大规模的攻击。本文将为大家详细介绍这次攻击的背景、影响以及应对措施。 攻击背景 最近&#xff0c;全球范围内遭受了一系列规模…

Fabric.js 样式不更新怎么办?

本文简介 带尬猴&#xff0c;我嗨德育处主任 不知道你有没有遇到过在使用 Fabric.js 时无意中一些骚操作修改了元素的样式&#xff0c;但刷新画布却没更新元素样式&#xff1f; 如果你也遇到同样的问题的话&#xff0c;可以尝试使用本文的方法。 是否需要重新绘制 我先举个例…

【Javascript】ajax(阿甲克斯)

目录 什么是ajax? 同步与异步 原理 注意 写一个ajax请求 创建ajax对象 设置请求方式和地址 发送请求 设置响应HTTP请求状态变化的函数 什么是ajax? 是基于javascript的一种用于创建快速动态网页的技术&#xff0c;是一种在无需重新加载整个网页的情况下&#xff0c…