c++ deque 的使用

news2024/12/16 13:41:36

 

目录

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 的时候再见!

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

MobPush数智化推送,精准定位万圣节狂欢年轻一族

随着中秋十一黄金周的结束&#xff0c;2023年最后一个法定节假日也一去不复返&#xff0c;但是别急&#xff0c;今年还有另一场不放假的狂欢节日——万圣节&#xff0c;万圣节作为西方国家第四季度最为重要的营销节日之一&#xff0c;在国内年轻人群体中同样具有较大的影响力和…

字节流和处理流的对象反序列化问题

细节&#xff1a; 读写要保持一致 序列对象时&#xff0c;默认将里面的所有属性都进行序列化&#xff0c;但除了static或transient修饰的成员 要求序列化或反序列化对象&#xff0c;需要实现Serializable 序列化对象时&#xff0c;要求里面的属性也要实现序列化接口 序列化…

java-- 静态数组

1.静态初始化数组 定义数组的时候直接给数组赋值。 2.静态初始化数组的格式&#xff1a; 注意&#xff1a; 1."数据类型[] 数组名"也可以写成"数据类型 数组名[]"。 2.什么类型的数组只能存放什么类型的数据 3.数组在计算机中的基本原理 当计算机遇到…

【优选算法精品】前缀和

文章目录 一、前缀和前缀和问题一维前缀和模板二维前缀和模板 细节处理题目1思路细节处理&#xff1a; 题目2思路 题目3题目4题目5题目6总结 一、前缀和 前缀和问题 前缀和用来快速解决某一段连续区间的和。 时间复杂度O(1) 注意&#xff1a;不要背模板&#xff0c;不要背模…

死锁Deadlock

定义 死锁是指两个或多个线程互相持有对方所需的资源&#xff0c;从而导致它们无法继续执行的情况。如下图所示&#xff0c;现有两个线程&#xff0c;分别是线程A及线程B&#xff0c;线程A持有锁A&#xff0c;线程B持有锁B。此时线程A想获取锁B&#xff0c;但锁B需等到线程B的结…

计算机网络之数据链路层(全)

[复习提示] 王道&#xff1a;本章是历年考试中考查的重点。要求在了解数据链路层基本概念和功能的基础上&#xff0c;重点掌握滑动窗口机制、三种可靠传输协议、各种MAC协议、HDLC协议和PPP协议&#xff0c;特别是CSMA/CD协议和以太网帧格式&#xff0c;以及局域网的争用期和最…

C++学习笔记之三(标准库、标准模板库、vector类)

C 1、C标准库2、C标准模板库2.1、vector2.1.1、vector与array2.1.2、vector与函数对象2.1.3、vector与迭代器 1、C标准库 C C C标准库指的是标准程序库( S t a n d a r d Standard Standard L i b a r a y Libaray Libaray)&#xff0c;它定义了十个大类&#xff0c;其中包括…

只有向内生长,才能向外绽放——中国人民大学与加拿大女王大学金融硕士

这个时代&#xff0c;产业迭代、竞争愈发激烈、世界局势变幻等这些都是我们无法控制的事。我们能做到的是不断地努力提升自己&#xff0c;成为所在领域的佼佼者。人的成长之路&#xff0c;就像是一场循序渐进的蜕变。不断努力是让自己逐步增值的过程。只有向内生长&#xff0c;…

世界经济论坛:ChatGPT等生成式AI,对全球23%岗位产生巨大影响

世界经济论坛与全球最大上市咨询公司之一埃森哲合作&#xff0c;联合发布了《未来工作&#xff1a;大语言模型与就业》白皮书。 世界经济论坛表示&#xff0c;随着ChatGPT、Midjourney、Github Copilot等生成式AI的飞速发展&#xff0c;对全球经济和劳动市场产生巨大影响。未来…

手写Vue渲染器render函数

使用js对象来描述UI更加的灵活。“这种对象”在vue框架中被称为虚拟DOM&#xff0c;渲染函数内部可以创建虚拟DOM&#xff0c;然后vue.js可以将其内容进行渲染。 1.渲染器的介绍 渲染器的作用就是把虚拟DOM渲染为真实DOM 思考下&#xff0c;我们有一个虚拟 DOM&#xff0c;如…

js数组的常用方法

目录 1. 添加/删除元素 2. 查找和遍历 3. 转换 4. 排序 5. 其他 JavaScript中的数组是一种用于存储多个值的对象。数组提供了一系列的方法来进行操作&#xff0c;包括添加、删除、遍历和搜索等。下面列举了一些JavaScript数组的常用方法&#xff1a; 1. 添加/删除元素 pu…

C指针 --- 进阶

目录 1. 字符指针 1.1. 一般使用 1.2. 另一种使用 2. 指针数组 3. 数组指针 3.1. 数组指针 3.2. 数组名和&数组名 3.3. 数组指针的用处 1. 传递一个数组 2. 传递数组首元素的地址 3. 数组指针处理一维数组 4. 数组指针处理二维数组 4. 数组传参和指针传参 4.1…

PHP危险函数

PHP危险函数 文章目录 PHP危险函数PHP 代码执行函数eval 语句assert()语句preg_replace()函数正则表达式里修饰符 回调函数call_user_func()函数array_map()函数 OS命令执行函数system()函数exec()函数shell_exec()函数passthru() 函数popen 函数反引号 实列 通过构造函数可以执…

杂牌行车记录仪特殊AVI结构恢复案例

最近遇到一个杂牌的行车记录仪需要恢复数据&#xff0c;其使用AVI格式&#xff0c;但是在扫描恢复的过程中却发现厂家对其AVI结构进行了“魔改”致程序无法正常识别 故障存储:16G SD卡 fat32文件系统 故障现象: 16G的SD卡&#xff0c;在发生事故后客户尝试自行接到手机上读…

小红书素人铺量推广费用是多少?

小红书作为一个拥有庞大用户群体的社交电商平台&#xff0c;在品牌营销和产品推广方面具有巨大的潜力。其中&#xff0c;素人铺量推广作为一种常见的推广方式&#xff0c;被越来越多的品牌和商家所采用。但是&#xff0c;对于很多刚刚接触小红书推广的人来说&#xff0c;素人铺…

二维码智慧门牌管理系统升级解决方案:一级属性 二级属性

文章目录 前言一、什么是智慧门牌管理系统&#xff1f;二、一级属性 vs. 二级属性三、升级中的实践意义 前言 在本文中&#xff0c;我们将深入探讨二维码智慧门牌管理系统的升级解决方案&#xff0c;特别聚焦于一级属性和二级属性的关键概念。我们将详细解释这些概念&#xff…

如何做好建筑翻译呢

近年来&#xff0c;随着跨国工程项目增加&#xff0c;建筑翻译也越来越受到重视。尤其是建筑图纸翻译在工程设计、规划和施工等方面都具有重要意义。那么&#xff0c;如何做好建筑翻译呢&#xff0c;建筑工程翻译哪个比较正规&#xff1f; 在建筑行业日新月异的发展中&#xff…

项目进度延误,危机管理5大注意事项

项目延误危机管理的重要性是不可忽视的。项目延误可能会导致资源浪费、成本增加、客户不满、信誉受损等一系列问题&#xff0c;严重影响项目的成功与效益。因此&#xff0c;有效地进行项目延误危机管理是至关重要的&#xff0c;一般主要是从以下5个方面进行管理&#xff1a; 1、…

python yield用法

用法和ts中的差不多 def test():i[1,2,3,4]for x in i:yield x for j in test():print(j) 使用的时候就会返回一次值 实际使用中可以这样使用&#xff0c;例如我把df按照半小时进行分割&#xff0c;然后分别取处理每半小时的数据 def test():final_df[raw_timestamp] pd.t…

js使用构造函数的注意点?

目录 前言 使用构造函数的注意点 1. 使用 new 关键字 用法 代码示例 理解 2. 构造函数首字母大写 用法 代码示例 理解 3. 在构造函数中返回对象 用法 代码示例 理解 4. 使用原型链 用法 代码示例 理解 总结 前言 JavaScript 是一种基于原型的语言&#xff…