C++ :STL中deque的原理

deque的结构类似于哈希表,使用一个指针数组存储固定大小的数组首地址,当数据分布不均匀时将指针数组内的数据进行偏移,桶不够用的时候会像vector一样扩容然后将之前数组中存储的指针拷贝过来,从原理可以看出deque的性能是非常高的,它不存咋像vector那样大规模的数据拷贝和大批量连续空间的需求同时也弥补了像list不支持随机访问的缺点,可以说deque集中了list和vector的大部分优点,当然缺点很致命在中间节点插入数据的时候会异常复杂,高效的前后端插入机制使得stack和queue都适配于deque。

vector动态数组

  • 支持随机访问
  • 可以自动扩容
  • 只支持末端插入

list双向链表

  • 不支持随机访问
  • 支持任意位置插入
  • 无需扩容

deque双端队列(vector< Array >)

  • 支持随机访问(伪随机)
  • 微量扩容
  • 支持前后端插入

看下面一段代码

#include<iostream>
#include<deque>
using namespace std;
struct T{~T(){cout<<"T析构";}}
void Add_head(deque<int>&q){q.push_front(1);cout<<&q.front()<<" "<<endl;
}void Add_tail(deque<int>&q){q.push_back(1);cout<<&q.back()<<" "<<endl;
}int main(){deque<int>q;for(int i=0;i<10;i++) Add_head(q);for(int i=0;i<10;i++) Add_tail(q);
}

在这里插入图片描述
从地址大概可以看出这个东西是一块一块的,块的大小是固定的,而且块内地址是连续的,想要获取更多的信息的话还要结合源码,网上有说deque是数组链表的,但如果简单的理解为链表的话就大错特错了,这么理解的话随机访问是实现不了的,我觉得理解为一种特殊的二维数组比较好。

下面看具体一些功能的实现

看下_Deque_val模板,_Block_size 表示单个数组元素个数,这里是根据元素的大小决定单个数组的大小,_Mapptr 是桶里面装一维数组的指针,_Mapsize桶的大小根据注释可以看到桶的扩容是指数级的,_Myoff存储偏移量,_Mysize存储元素个数
注意:这个偏移量是针对_Mapptr 的头部开始到当前元素的偏移量,此外单个数组的大小受限于数据大小,所以很多时候双端队列会表现的像维护在指针数组上的链表

// CLASS TEMPLATE _Deque_val
template <class _Val_types>
class _Deque_val : public _Container_base12 {
public:using value_type      = typename _Val_types::value_type;using size_type       = typename _Val_types::size_type;using difference_type = typename _Val_types::difference_type;using pointer         = typename _Val_types::pointer;using const_pointer   = typename _Val_types::const_pointer;using reference       = value_type&;using const_reference = const value_type&;using _Mapptr         = typename _Val_types::_Mapptr;
private:static constexpr size_t _Bytes = sizeof(value_type);
public:static constexpr int _Block_size =_Bytes <= 1 ? 16 : _Bytes <= 2 ? 8 : _Bytes <= 4 ? 4 : _Bytes <= 8 ? 2 : 1; // elements per block (a power of 2)_Deque_val() noexcept : _Map(), _Mapsize(0), _Myoff(0), _Mysize(0) {}size_type _Getblock(size_type _Off) const noexcept {// NB: _Mapsize and _Block_size are guaranteed to be powers of 2return (_Off / _Block_size) & (_Mapsize - 1);}_Mapptr _Map; // pointer to array of pointers to blockssize_type _Mapsize; // size of map array, zero or 2^Nsize_type _Myoff; // offset of initial elementsize_type _Mysize; // current length of sequence
};

这个是_Mapptr 这个桶的基类,可以看到使用的是二级指针,它的作用就是维护一种二维的结构,这时候可能好奇了它作为一个单独的模板是如何适配作为_Deque_val 模板类型的一部分的。

template <class _Ty>
struct _Deque_simple_types : _Simple_types<_Ty> {using _Mapptr = _Ty**;
};

这个适配器完成了适配工作,将多个类适配为一个类很大程度上提高了代码的可读性,使得代码可以更高的遵循开闭原则。

template <bool _Test, class _Ty1, class _Ty2>
struct conditional { // Choose _Ty1 if _Test is true, and _Ty2 otherwiseusing type = _Ty1;
};
template <class _Ty1, class _Ty2>
struct conditional<false, _Ty1, _Ty2> {using type = _Ty2;
};
template <bool _Test, class _Ty1, class _Ty2>
using conditional_t = typename conditional<_Test, _Ty1, _Ty2>::type;

deque中迭代器访问元素,使用偏移量获取存储的行和列。

_NODISCARD reference operator*() const noexcept {const auto _Mycont = static_cast<const _Mydeque*>(this->_Getcont());
#if _ITERATOR_DEBUG_LEVEL != 0_STL_VERIFY(_Mycont, "cannot dereference value-initialized deque iterator");_STL_VERIFY(_Mycont->_Myoff <= this->_Myoff && this->_Myoff < _Mycont->_Myoff + _Mycont->_Mysize,"cannot deference out of range deque iterator");
#endif // _ITERATOR_DEBUG_LEVEL != 0_Size_type _Block = _Mycont->_Getblock(_Myoff);_Size_type _Off   = _Myoff % _Block_size;return _Mycont->_Map[_Block][_Off];}

那么现在就有一个问题,如果插入元素时集中在头部或者尾部时它会直接扩容吗?带着问题接着分析。

首先看一下emplace_back函数

template <class... _Tys>void _Emplace_back_internal(_Tys&&... _Vals) {if ((_Myoff() + _Mysize()) % _Block_size == 0 && _Mapsize() <= (_Mysize() + _Block_size) / _Block_size) {_Growmap(1);}_Myoff() &= _Mapsize() * _Block_size - 1;size_type _Newoff = _Myoff() + _Mysize();size_type _Block  = _Getblock(_Newoff);if (_Map()[_Block] == nullptr) {_Map()[_Block] = _Getal().allocate(_Block_size);}_Alty_traits::construct(_Getal(), _Unfancy(_Map()[_Block] + _Newoff % _Block_size), _STD forward<_Tys>(_Vals)...);++_Mysize();}

可以看到整体逻辑和vector差不多,emplace_front也差不多就不看了,因为站在函数角度它的不需要关注是头插还是尾插。
这里的策略依旧是可以插入时插入不能插入时进行处理,直接看扩容处理函数,和直接注释的一样,如果没有可插入的空间时桶的大小变为原来的二倍,然后将指针挪到新的数组的中间即可,偏移量这些重新计算,这个环节虽然看起来很麻烦但是注意这里是针对指针的而不是针对元素的,所以说效率还是蛮高的。

void _Growmap(size_type _Count) { // grow map by at least _Count pointers, _Mapsize() a power of 2static_assert(1 < _Minimum_map_size, "The _Xlen() test should always be performed.");_Alpty _Almap(_Getal());size_type _Newsize = 0 < _Mapsize() ? _Mapsize() : 1;while (_Newsize - _Mapsize() < _Count || _Newsize < _Minimum_map_size) {// scale _Newsize to 2^N >= _Mapsize() + _Countif (max_size() / _Block_size - _Newsize < _Newsize) {_Xlen(); // result too long}_Newsize *= 2;}_Count = _Newsize - _Mapsize();size_type _Myboff = _Myoff() / _Block_size;_Mapptr _Newmap   = _Almap.allocate(_Mapsize() + _Count);_Mapptr _Myptr    = _Newmap + _Myboff;_Myptr = _STD uninitialized_copy(_Map() + _Myboff, _Map() + _Mapsize(), _Myptr); // copy initial to endif (_Myboff <= _Count) { // increment greater than offset of initial block_Myptr = _STD uninitialized_copy(_Map(), _Map() + _Myboff, _Myptr); // copy rest of old_Uninitialized_value_construct_n_unchecked1(_Myptr, _Count - _Myboff); // clear suffix of new_Uninitialized_value_construct_n_unchecked1(_Newmap, _Myboff); // clear prefix of new} else { // increment not greater than offset of initial block_STD uninitialized_copy(_Map(), _Map() + _Count, _Myptr); // copy more old_Myptr = _STD uninitialized_copy(_Map() + _Count, _Map() + _Myboff, _Newmap); // copy rest of old_Uninitialized_value_construct_n_unchecked1(_Myptr, _Count); // clear rest to initial block}_Destroy_range(_Map() + _Myboff, _Map() + _Mapsize());if (_Map() != _Mapptr()) {_Almap.deallocate(_Map(), _Mapsize()); // free storage for old}_Map() = _Newmap; // point at new_Mapsize() += _Count;}

stack和queue其实都是deque的适配器类,下面举个例子

template<class T,class dq=deque<T>>
class stk{
protected:dq d;
public:void pop(){d.pop_back();}void push(const T& val){d.push_back(val);}const T& top()const{return d.back();}
};

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

docker部署-RabbitMq

1. 参考 RabbitMq官网 docker官网 2. 拉取镜像 这里改为自己需要的版本即可&#xff0c;下面容器也需要同理修改 docker pull rabbitmq:3.12-management3. 运行容器 docker run \ --namemy-rabbitmq-01 \ -p 5672:5672 \ -p 15672:15672 \ -d \ --restart always \ -…

java入门学习Day01

本篇文章主要是学会如何使用IDEA&#xff0c;和运行第一个java文件。 java环境安装&#xff1a;Windows下Java环境配置教程_windows java环境配置-CSDN博客 IDEA安装&#xff1a;IDEA 2023.2.5 最新激活码,注册码&#xff08;亲测好用&#xff09; - 异常教程 以上两个链接…

C++—vector的介绍及使用 vector的模拟实现

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 目录 文章目录 前言 一、vector的介绍及使用 1.1 vector的介绍 1.2 vector的使用 1.2.1 vector的定义 1.2.2 vector iterator 的使用 1.2.3 vector 空间增长问题 1.2.4 vecto…

JVM 八股(一)

JVM 1.类装载的执行过程 加载&#xff1a; 元空间存储构造函数&#xff0c;方法&#xff0c;字段等 验证 准备 解析 初始化 使用 2.垃圾回收 什么是垃圾回收&#xff1f;怎样找到这些垃圾&#xff1f;找到垃圾后是怎么清除的&#xff08;垃圾回收算法&#xff09;&#x…

一篇搞定AVL树+旋转【附图详解旋转思想】

&#x1f389;个人名片&#xff1a; &#x1f43c;作者简介&#xff1a;一名乐于分享在学习道路上收获的大二在校生 &#x1f648;个人主页&#x1f389;&#xff1a;GOTXX &#x1f43c;个人WeChat&#xff1a;ILXOXVJE &#x1f43c;本文由GOTXX原创&#xff0c;首发CSDN&…

【C语言】贪吃蛇【附源码】

欢迎来到英杰社区https://bbs.csdn.net/topics/617804998 一、游戏说明&#xff1a; 一个基于C语言链表开发的贪吃蛇游戏&#xff1a; 1. 按方向键上下左右&#xff0c;可以实现蛇移动方向的改变。 2. 短时间长按方向键上下左右其中之一&#xff0c;可实现蛇向该方向的短时间…

智能指针(C++11)

智能指针的使用 问题 我们在平时写程序的时候&#xff0c;有些情况下不可避免地会遇见内存泄露的情况。内存泄露是指因为疏忽或错误&#xff0c;造成程序未能释放已经不再使用的内存的情况。例如下面这个例子&#xff0c;内存泄漏不易被察觉。 int div() {int a, b;cin >…

【复习linux相关命令】

查看命令位置&#xff0c;查找命令 which命令 查看命令的位置 [rootVM-12-15-opencloudos ~]# which cd /usr/bin/cd [rootVM-12-15-opencloudos ~]# which java /usr/local/java/jdk1.8.0_261/bin/java [rootVM-12-15-opencloudos ~]# which pwd /usr/bin/pwdfind查找文件 …

element+Vue2,在一个页面跳转到另一个页面,并自动选中table的某一行

需求&#xff1a;点击A页面的某处&#xff0c;跳转到B页面并选中B页面表格的某一行&#xff08;点击B页面的搜索后需要清空默认选择的状态&#xff09;环境&#xff1a;vue2、element的table&#xff0c;table允许多选知识点&#xff1a;主要使用到table的这两属性&#xff1a;…

Diffusion添加噪声noise的方式有哪些?怎么向图像中添加噪声?

添加噪声的方式大致分为两种&#xff0c;一种是每张图像在任意timestep都加入一样的均匀噪声&#xff0c;另一种是按照timestep添加不同程度的噪声 一、在任意timestep都加入一样的noise batch_size 32x_start torch.rand(batch_size,3,256,256) noise torch.randn_like(x_…

亚信安全荣获2023年度5G创新应用评优活动两项大奖

近日&#xff0c;“关于2023 年度5G 创新应用评优活动评选结果”正式公布&#xff0c;亚信安全凭借在5G安全领域的深厚积累和创新实践&#xff0c;成功荣获“5G技术创新的优秀代表”和“5G应用创新的杰出实践”两项大奖。 面向异构安全能力的5G安全自动化响应系统 作为5G技术创…

【Mybatis 基础】增删改查(@Insert, @Delete, @Update, @Select)

Mybatis Insert Delete Update Select Mybatis用法基础操作 - 删除delete 传参SpringbootMybatisCrudApplicationTests 测试类删除预编译SQL 基础操作 - 插入Insert 插入SpringbootMybatisCrudApplicationTests 测试类插入对象主键返回 基础操作 - 更新UPDATE 更新SpringbootMy…

Python进阶编程 --- 1.类和对象

文章目录 第一章&#xff1a;1.初始对象1.1 使用对象组织数据1.2 类的成员方法1.2.1 类的定义和使用1.2.2 创建类对象1.2.3 成员变量和成员方法1.2.4 成员方法的定义语法1.2.5 注意事项 1.3 类和对象1.3.1 基于类创建对象 1.4 构造方法1.5 其他内置方法1.5.1 魔术方法str字符串…

(南京观海微电子)——DDIC显示触控芯片介绍

显示驱动芯片&#xff08;Display Driver Integrated Circuit&#xff0c;简称DDIC&#xff09;的主要功能是控制OLED显示面板。它需要配合OLED显示屏实现轻薄、弹性和可折叠&#xff0c;并提供广色域和高保真的显示信号。同时&#xff0c;OLED要求实现比LCD更低的功耗&#xf…

成绩管理系统|基于springboot成绩管理系统的设计与实现(附项目源码+论文)

基于springboot成绩管理系统的设计与实现 一、摘要 传统办法管理信息首先需要花费的时间比较多&#xff0c;其次数据出错率比较高&#xff0c;而且对错误的数据进行更改也比较困难&#xff0c;最后&#xff0c;检索数据费事费力。因此&#xff0c;在计算机上安装毕业设计成绩管…

平衡二叉树(AVL树)

文章目录 平衡二叉树&#xff08;AVL树&#xff09;1、平衡二叉树概念2、平衡二叉树的的实现2.1、平衡二叉树的结点定义2.2、平衡二叉树的插入2.3、平衡二叉树的旋转2.3.1、右单旋&#xff08;R旋转&#xff09;2.3.2、左单旋&#xff08;L旋转&#xff09;2.3.3、先右单旋再左…

leetcode 周赛 391场

2. 换水问题 给你两个整数 numBottles 和 numExchange 。 numBottles 代表你最初拥有的满水瓶数量。在一次操作中&#xff0c;你可以执行以下操作之一&#xff1a; 喝掉任意数量的满水瓶&#xff0c;使它们变成空水瓶。用 numExchange 个空水瓶交换一个满水瓶。然后&#xf…

Django安装及第一个项目

1、安装python C:\Users\leell>py --version Python 3.10.6 可以看出我的环境python的版本3.10.6&#xff0c;比较新 2、 Python 虚拟环境创建 2.1 官网教程 目前&#xff0c;有两种常用工具可用于创建 Python 虚拟环境&#xff1a; venv 在 Python 3.3 及更高版本中默…

Vue系列——数据对象

<!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>el:挂载点</title> </head> <body&g…

C++11入门手册第二节,学完直接上手Qt(共两节)

C++多线程 #include <thread>:C++多线程库 #include <mutex>:C++互斥量库 #include <future>:C++异步库 多线程介绍 线程的创建 void entry_1() { }以普通函数作为线程入口函数:void entry_2(int val) { }​std::thread my_thread_1(entry_1);std::thr…