C++ STL:deque使用及源码剖析

  Deque是一种双向开口的连续线性空间。能在头尾两端分别做元素的插入和删除,而且是在常数的时间内完成。虽然Vector也可以在首端进行元素的插入和删除(利用insert和erase),但效率差(涉及到整个数组的移动),无法被接受。 

 

deque的定义

 #include <deque> // deque属于std命名域的,因此需要通过命名限定

deque<int> a; // 定义一个int类型的双端队列a

deque<int> a(10); // 定义一个int类型的双端队列a,并设置初始大小为10

deque<int> a(10, 1); // 定义一个int类型的双端队列a,并设置初始大小为10且初始值都为1

deque<int> b(a); // 定义并用双端队列a初始化双端队列b

deque<int> b(a.begin(), a.begin()+3); // 将双端队列a中从第0个到第2个(共3个)作为双端队列b的初始值

int n[] = { 1, 2, 3, 4, 5 };

deque<int> a(n, n + 5);

deque<int> a(&n[1], &n[4]); // 将n[1]、n[2]、n[3]作为双端队列a的初值

deque的使用 

操作

描述

deq.size()

返回容器中的元素数量。

deq.max_size()

返回容器可能存储的最大元素数量。

deq.resize(n)

改变容器的大小为 n 个元素。如果 n 大于当前大小,新位置会被默认插入元素进行填充。

deq.empty()

检查容器是否为空。

deq.push_front(const T& x)

在容器的头部插入一个元素 x

deq.push_back(const T& x)

在容器的末尾添加一个元素 x

deq.insert(iterator it, const T& x)

在迭代器 it 指定的位置插入一个元素 x

deq.insert(iterator it, int n, const T& x)

在迭代器 it 指定的位置插入 n 个相同的元素 x

deq.insert(iterator it, iterator first, iterator last)

在迭代器 it 指定的位置插入另一个容器的 [first, last) 区间的数据。

deq.pop_front()

删除容器头部的一个元素。

deq.pop_back()

删除容器末尾的一个元素。

deq.erase(iterator it)

删除迭代器 it 指定的元素。

deq.erase(iterator first, iterator last)

删除迭代器 [first, last) 区间的元素。

deq.clear()

删除容器中的所有元素。

deq[1]

通过下标访问元素,不检查越界。

deq.at(1)

通过 at 方法访问元素,如果越界会抛出异常。

deq.front()

访问第一个元素。

deq.back()

访问最后一个元素。

deq.assign(int nSize, const T& x)

nSize 个 x 值的副本替换容器中的内容。

swap(deque&)

交换两个同类型容器的元素。

deq.begin()

返回指向容器第一个元素的迭代器。

deq.end()

返回指向容器最后一个元素之后的迭代器。

deq.cbegin()

返回指向容器第一个元素的常量迭代器。

deq.cend()

返回指向容器最后一个元素之后的常量迭代器。

deq.rbegin()

返回指向容器最后一个元素的反向迭代器。

deq.rend()

返回指向容器第一个元素前面位置的反向迭代器。

#include <iostream>
#include <deque>int main() {std::deque<int> deq;// 头部添加元素deq.push_front(1);// 末尾添加元素deq.push_back(2);// 在任意位置插入一个元素auto it = deq.begin();deq.insert(it + 1, 3); // 在第一个元素之后插入3// 在任意位置插入 n 个相同的元素deq.insert(it, 2, 4); // 在头部插入两个4// 插入另一个deque的[first, last)间的数据std::deque<int> another_deq{5, 6, 7};deq.insert(deq.end(), another_deq.begin(), another_deq.end());// 头部删除元素deq.pop_front();// 末尾删除元素deq.pop_back();// 任意位置删除一个元素deq.erase(deq.begin() + 1); // 删除第二个元素// 删除[first, last)之间的元素deq.erase(deq.begin(), deq.begin() + 2); // 删除前两个元素// 清空所有元素deq.clear();// 多个元素赋值deq.assign(3, 8); // deq现在有三个元素,每个都是8// 交换两个同类型容器的元素std::deque<int> deq2;deq2.push_back(9);deq.swap(deq2);// 访问第一个元素和最后一个元素std::cout << "Front: " << deq.front() << ", Back: " << deq.back() << std::endl;// 使用迭代器遍历dequestd::cout << "Contents of deq:";for(auto iter = deq.begin(); iter != deq.end(); ++iter) {std::cout << ' ' << *iter;}std::cout << std::endl;// 使用下标访问if (!deq.empty()) {std::cout << "Element at index 0: " << deq[0] << std::endl;std::cout << "Element at index 0 (using at): " << deq.at(0) << std::endl;}// 使用反向迭代器遍历dequestd::cout << "Contents of deq in reverse:";for(auto riter = deq.rbegin(); riter != deq.rend(); ++riter) {std::cout << ' ' << *riter;}std::cout << std::endl;return 0;
}

 deque数据结构

deque维护一个指向map的指针外和 start和finish两个迭代器,分别指向第一缓冲区的第一个元素和最后缓冲区的最后一个元素的下一位置。

template <class T, class Alloc = alloc, size_t BufSiz = 0>
class deque {
public:typedef T value_type;typedef value_type* pointer;typedef size_t size_type;typedef pointer* map_pointer;
public:typedef __deque_iterator<T, T&, T*, BufSiz>  iterator;
protected:iterator start;    // 第一个节点iterator finish;   // 最后一个结点map_pointer map;size_type map_size;
public:iterator begin() { return start; }iterator end() { return finish; }reference operator[](size_type n) { return start[difference_type(n)]; } // 调用迭代器重载的operator[]// ...
}
  • 指针数组 (map):是一个指针数组,维护指向缓冲区的指针,这些缓冲区构成了 deque 的内存空间。map 左右两边预留有空间,允许前后扩展。
  • 迭代器 (start, finish):分别指向 deque 中第一个元素和最后一个元素之后的位置,通过迭代器可以快速访问或修改 deque 的元素。
  • map_size 表示 map 中指针的数量,即数据块的总数。
  • map 的左右两边留有剩余空间是 deque 设计的一个关键特点。这些剩余空间允许快速地在 deque 的前端或后端添加新的分段,而无需重新分配整个 map。这样,即使是在 deque 的两端插入或删除元素,操作的时间复杂度也能保持在常数时间内。
  • erase 和 insert 函数实现了元素的删除和插入操作,它们采用了不同的策略来最小化元素移动的开销。具体策略取决于操作位置相对于 deque 中间位置的不同,以减少需要移动的元素数量。

 当 deque 需要扩展其存储空间时,它会分配一个新的 map,这个新 map 有更多的指针,可以指向更多的缓冲区。然后,deque 会将现有的缓冲区指针从旧 map 复制到新 map 中,并适当地调整 start 和 finish 迭代器,以反映新的布局。这个过程使得 deque 能够在维持高效插入和删除操作的同时,提供对元素的快速随机访问。

当 deque 的一个分段被填满时,它会动态地添加一个新的分段来存储更多的元素。同样地,如果 deque 的大小减少,一些分段可能会被释放以节约空间。deque 动态调整其分段的能力,使得它在存储大量数据时非常灵活和高效。

 deque迭代器

    deque 使用特化的迭代器(__deque_iterator),以适应其复杂的内部结构。这个迭代器能够跨越不同的数据块,为外部提供连续访问的接口。

template <class T, class Ref, class Ptr, size_t BufSiz>
struct __deque_iterator {    //并未继承std::iterator,为了符合STL规范,要对五个相应类型都做定义typedef __deque_iterator<T, T&, T*, BufSiz>             iterator;typedef __deque_iterator<T, const T&, const T*, BufSiz> const_iterator;static size_t buffer_size() { return __deque_buf_size(BufSiz, sizeof(T)); }typedef random_access_iterator_tag iterator_category;    //1typedef T value_type;    //2typedef Ptr pointer;    //3typedef Ref reference;    //4typedef size_t size_type;typedef ptrdiff_t difference_type;    //5typedef T** map_pointer;typedef __deque_iterator self;//保持与空间的联结T* cur;        //所指是当前缓冲区里的当前元素T* first;    //所指是当前缓冲区里的头元素T* last;    //所指是当前缓冲区里的尾元素的下一位(含备用空间)map_pointer node;    //指向map的头...
}

__deque_iterator 结构体

类型定义

  • 迭代器对外提供了与 STL 兼容的五种类型定义,包括迭代器类别(iterator_category),值类型(value_type),指针类型(pointer),引用类型(reference),以及差异类型(difference_type)。
  • 静态成员函数 buffer_size计算并返回每个缓冲区(即段)可以容纳的元素数量。
  • cur:指向当前缓冲区中当前元素的指针。
  • first:指向当前缓冲区第一个元素的指针。
  • last:指向当前缓冲区最后一个元素之后位置的指针,即标记缓冲区结束的边界。
  • node:指向当前缓冲区所在 map 中的指针,即指向管理当前缓冲区指针的 map 元素。

   当迭代器前进(++)或后退(--)时,它首先检查是否即将超出当前缓冲区的边界(通过比较 cur 与 first 或 last)。如果是这种情况,迭代器会跳转到相邻的缓冲区(通过修改 node 指向的指针),并相应地更新 cur、first 和 last,以指向新缓冲区的正确位置。

   这种设计使得迭代器能够平滑地跨越 deque 的分段存储,对使用者隐藏了复杂的内部结构。用户可以像操作一个连续存储的数组那样,使用 deque 迭代器进行随机访问和遍历,而不需要关心背后的分段逻辑。迭代器抽象了 deque 的分段存储细节,使得从用户的角度看,deque 像是一个完全连续的数据结构。

参考

《STL源码剖析》

https://www.cnblogs.com/linuxAndMcu/p/10260124.html

https://www.cnblogs.com/MisakiJH/p/11765567.html

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

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

相关文章

代码随想录 Leetcode56. 合并区间

题目&#xff1a; 代码(首刷自解 2024年2月18日&#xff09;&#xff1a; 这题与气球扎针&#xff0c;删除重复的大体逻辑相似。需要额外定义些变量来存储头尾 class Solution { private:const static bool cmp(vector<int>& a, vector<int>& b) {return …

001 QGIS介绍

Quantum GIS&#xff08;QGIS&#xff09;是开源地理信息系统桌面软件&#xff0c;使用GNU&#xff08;General Public License&#xff09;授权&#xff0c; 属于 Open Source eospatial Foundation&#xff08;OSGeo&#xff09;的官方计划。在 GNU 授权下&#xff0c;开发者…

Postman路径修改

默认安装好Postman之后&#xff0c;默认路径在&#xff1a;C:\Users\用户名\AppData\Local\Postman。 修改路径只需要将整个文件夹拷贝到需要移动的位置即可&#xff0c;然后重新创建一个快捷方式。再删除原来路径的文件夹。

C++ //练习 7.29 修改你的Screen类,令move、set和display函数返回Screen并检查程序的运行结果,在上一个练习中你的推测正确吗?

C Primer&#xff08;第5版&#xff09; 练习 7.29 练习 7.29 修改你的Screen类&#xff0c;令move、set和display函数返回Screen并检查程序的运行结果&#xff0c;在上一个练习中你的推测正确吗&#xff1f; 环境&#xff1a;Linux Ubuntu&#xff08;云服务器&#xff09; …

RIP协议详解

​RIP是最早的动态路由协议&#xff0c;虽然已经过时并且很少使用&#xff0c;但是可以通过学习RIP并且和ospf等现在正在使用的路由协议对比&#xff0c;了解其工作原理和过时原因&#xff0c;具有很强的学习性。 一、RIP协议简介 RIP&#xff08;Routing Information Protoc…

Bpmn-js 属性控制

我们可以通过bpmn-js来访问对应的BPMN图例的属性信息。对应的流程图中的每个图例元素&#xff08;如开始、结束、中间/边界事件等都通过businessObject属性存储对基础BPMN元素的引用。业务对象是从BPMN 2.0 XML导入并在导出过程中序列化的实际元素。使用业务对象来读取和写入BP…

如何减少HTTP请求次数

资料来源 : 小林coding 小林官方网站 : 小林coding (xiaolincoding.com) 如何减少HTTP请求次数? 减少 HTTP 请求次数自然也就提升了 HTTP 性能&#xff0c;可以从这 3 个方面入手: 减少重定向请求次数合并请求延迟发送请求 减少重定向请求次数 我们先来看看什么是重定向请…

美相关 APT 组织分析报告

获取方式&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1AsysdggUIbvB3PZ41MaJaQ?pwd8euh 提取码&#xff1a;8euh

Debug Monitor中断详细解析

文章目录 0 基本术语1 相关寄存器和指令1.1 Debug Halting Control and Status Register (DHCSR), 0xE000EDF01.2 Debug Exception and Monitor Control Register (DEMCR), 0xE000EDFC1.3 Debug Fault Status Register, DFSR, 0xE000ED301.4 BKPT指令 2 Debug Monitor中断示例2…

解读OpenAI视频生成模型Sora背后的原理:Diffusion Transformer

Diffusion Models视频生成-博客汇总 前言&#xff1a;OpenAI最近推出的视频生成模型Sora在效果上实现了真正的遥遥领先&#xff0c;很多博主都介绍过Sora&#xff0c;但是深入解读背后原理的博客却非常少。Sora的原理最主要的是核心模型主干《Scalable Diffusion Models with T…

Code Composer Studio (CCS) - Breakpoint (断点)

Code Composer Studio [CCS] - Breakpoint [断点] 1. BreakpointReferences 1. Breakpoint 选中断点右键 -> Breakpoint Properties… Skip Count&#xff1a;跳过断点总数&#xff0c;在断点执行之前设置总数 Current Count&#xff1a;当前跳过断电累计值 References […

CCF编程能力等级认证GESP—C++7级—20231209

CCF编程能力等级认证GESP—C7级—20231209 单选题&#xff08;每题 2 分&#xff0c;共 30 分&#xff09;判断题&#xff08;每题 2 分&#xff0c;共 20 分&#xff09;编程题 (每题 25 分&#xff0c;共 50 分)商品交易纸牌游戏 答案及解析单选题判断题编程题1编程题2 单选题…

Vue实现多个input输入,光标自动聚焦到下一个input

遇到一个需求&#xff0c;需要实现和移动端短信输入一样&#xff0c;输入内容后&#xff0c;光标会进入下一个输入框 需要用到2个事件 keydown事件发生在键盘的键被按下的时候 keyup 事件在按键被释放的时候触发 <template><div class"box"><el-fo…

OpenAI重磅发布Sora——首个视频生成模型:利用文本-视频人工智能将想象变为现实

想象一下&#xff0c;现在你有一段文本话描述的故事&#xff0c;通过输入这段文本&#xff0c;就可以立刻展开一个生动详细的视频。这就是 OpenAI 神奇的 Sora&#xff0c;一个革命性的文本到视频的 AI 模型。Sora于2024年2月推出&#xff0c;凭借其仅凭文字提示就能生成现实和…

CSS 不同颜色的小圆角方块组成的旋转加载动画

<template><!-- 创建一个装载自定义旋转加载动画的容器 --><view class="spinner"><!-- 定义外部包裹容器,用于实现整体旋转动画 --><view class="outer"><!-- 定义四个内部小方块以形成十字形结构 --><view clas…

攻防演练后的一点随记

攻防演练 攻防演练算是告一段落了&#xff0c;各位红队和蓝队的兄弟们都辛苦了&#xff0c;写一点随记&#xff0c;供大家参考。 记得第一次参加攻防演练是在2018年&#xff0c;当时被派到北京&#xff0c;在某个政企单位做攻防演练支撑工作&#xff0c;然后2020年又被紧急派到…

Vue首屏优化,12个提速建议

文章目录 代码拆分和懒加载&#xff1a;代码拆分懒加载 图片优化&#xff1a;组件懒渲染&#xff1a;数据预获取和缓存&#xff1a;服务器端渲染&#xff08;SSR&#xff09;&#xff1a;代码压缩和合并&#xff1a;使用 CDN 加速&#xff1a;监控和性能分析&#xff1a;代码优…

AIGC实战——能量模型(Energy-Based Model)

AIGC实战——能量模型 0. 前言1. 能量模型1.1 模型原理1.2 MNIST 数据集1.3 能量函数 2. 使用 Langevin 动力学进行采样2.1 随机梯度 Langevin 动力学2.2 实现 Langevin 采样函数 3. 利用对比散度训练小结系列链接 0. 前言 能量模型 (Energy-based Model, EBM) 是一类常见的生…

c++开发基础之保障多线程编程中的原子操作InterlockedIncrement和InterlockedDecrement用法详解

一、介绍 在多线程编程中&#xff0c;确保对共享变量进行原子操作是至关重要的。当多个线程同时访问和修改同一共享资源时&#xff0c;如果没有合适的同步机制&#xff0c;可能会导致数据竞争、内存一致性问题&#xff0c;甚至造成程序崩溃。为了解决这个问题&#xff0c;C提供…

公众号,h5 链接直接在浏览器打开 拒绝下载视频解决方案

公众号&#xff0c;h5 链接直接在浏览器打开 拒绝下载视频 1.微信打开公众号 2.在微信上打开浏览器 3.F12打开页面 4.播放视频 5.找到video标签代码 6.去掉video标签的属性controlslist“nodownload” 7.全屏播放 8.下载