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

  • 10
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
dequeC++标准库中的容器,它是一种双端队列(double-ended queue),可以在两端进行插入和删除操作。在引用中的代码示例中,通过包含头文件<iostream>和<deque>,使用命名空间std来定义了一个名为test的函数。在test函数中,创建了一个deque对象d,并通过d.push_back()函数将元素添加到队列的末尾。然后,使用deque对象d的迭代器构造了一个新的deque对象d2。接着,通过d2.push_back()将10000作为新元素添加到了d2的末尾。最后,通过d.swap(d2)交换了两个deque对象的内容,并通过d.back()获取了d队列中最后一个元素的值,即10000。输出结果为10000。 在引用中的代码示例中,使用deque对象d的成员函数front()和back()分别返回队列的第一个元素和最后一个元素的值。另外,deque对象d的成员函数insert()可以在指定位置插入一个或多个元素,而erase()可以删除指定位置的一个或多个元素。 应用sort算法对deque进行排序时,可以通过包含头文件<algorithm>,定义一个回调函数compare,该函数用于自定义排序规则,可以根据需要按升序或降序排序。然后,使用sort函数对deque进行排序,并通过printDeque函数输出排序后的deque元素。 除此之外,deque还有一些其他函数,如swap()可以交换两个deque对象的内容,assign()可以将一个给定值赋值给deque中的指定位置。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [C++ deque](https://blog.csdn.net/weixin_59141600/article/details/126898400)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *3* [C++ deque用法详解](https://blog.csdn.net/qq_39779233/article/details/107983598)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

姬如祎

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值