C++中的deque详解

1. 引言

在C++标准模板库(STL)中,deque(双端队列)是一个非常重要的容器,它支持在序列的两端进行快速插入和删除操作。与vector不同,deque不需要在内存中连续存储元素,因此它对于需要在序列中间进行大量插入和删除操作的情况更为高效。本文将详细介绍deque的使用方法、特性以及提供示例代码。

2. deque的基本特性

2.1 容器特性

  • 动态数组deque可以看作是一个动态数组,其大小可以在运行时改变。
  • 双端操作:在deque的头部和尾部插入或删除元素的时间复杂度都是O(1)。
  • 非连续存储:与vector不同,deque的元素不一定在内存中连续存储。这使得deque在序列中间插入或删除元素时更加高效。

2.2 迭代器

deque提供了随机访问迭代器,支持使用[]操作符和at()函数来访问元素。同时,也支持使用迭代器进行遍历和修改元素。

2.3 成员函数

deque提供了丰富的成员函数,用于执行各种操作,如插入、删除、修改和查找元素。

3. deque的基本操作

3.1 创建和初始化

#include <iostream>
#include <deque>int main() {// 创建一个空的dequestd::deque<int> intDeque;// 创建一个包含初始元素的dequestd::deque<int> intDeque2 = {1, 2, 3, 4, 5};// 使用迭代器初始化dequestd::deque<int> intDeque3(intDeque2.begin(), intDeque2.end());return 0;
}

3.2 插入元素

int main() {std::deque<int> intDeque;// 在尾部插入元素intDeque.push_back(1);// 在头部插入元素intDeque.push_front(0);// 在指定位置插入元素intDeque.insert(intDeque.begin() + 1, 2);// 使用迭代器范围插入元素std::deque<int> temp = {3, 4};intDeque.insert(intDeque.end(), temp.begin(), temp.end());return 0;
}

3.3 删除元素

int main() {std::deque<int> intDeque = {0, 1, 2, 3, 4};// 删除尾部元素intDeque.pop_back();// 删除头部元素intDeque.pop_front();// 删除指定位置的元素intDeque.erase(intDeque.begin() + 1);// 使用迭代器范围删除元素intDeque.erase(intDeque.begin() + 1, intDeque.end() - 1);return 0;
}

3.4 访问元素

int main() {std::deque<int> intDeque = {0, 1, 2, 3, 4};// 使用下标访问元素std::cout << "Element at index 2: " << intDeque[2] << std::endl;// 使用at函数访问元素(更安全,会检查索引是否越界)std::cout << "Element at index 3: " << intDeque.at(3) << std::endl;// 使用迭代器访问元素for (std::deque<int>::iterator it = intDeque.begin(); it != intDeque.end(); ++it) {std::cout << *it << " ";}std::cout << std::endl;// 使用范围for循环访问元素for (int val : intDeque) {std::cout << val << " ";}std::cout << std::endl;return 0;
}

3.5 修改元素

int main() {std::deque<int> intDeque = {0, 1, 2, 3, 4};// 使用下标修改元素intDeque[2]= 10;// 使用迭代器修改元素for (std::deque<int>::iterator it = intDeque.begin(); it != intDeque.end(); ++it) {if (*it == 10) {*it = 20; // 假设我们要将第一个找到的10修改为20break; // 找到后退出循环}}// 使用范围for循环和引用修改元素for (int& val : intDeque) {if (val == 20) {val = 30; // 假设我们要将第一个找到的20修改为30break; // 找到后退出循环}}// 打印修改后的dequefor (int val : intDeque) {std::cout << val << " ";}std::cout << std::endl;return 0;
}

3.6 查找元素

#include <algorithm> // 需要包含这个头文件来使用std::findint main() {std::deque<int> intDeque = {0, 1, 2, 3, 4};// 使用std::find查找元素auto it = std::find(intDeque.begin(), intDeque.end(), 3);if (it != intDeque.end()) {std::cout << "Element found at: " << std::distance(intDeque.begin(), it) << std::endl;} else {std::cout << "Element not found." << std::endl;}return 0;
}

3.7 其他成员函数

deque还提供了许多其他有用的成员函数,如size()(返回元素数量)、empty()(检查deque是否为空)、clear()(清除所有元素)、resize()(改变deque的大小)等。

4. deque的内存管理

deque的内存管理是其与vector的主要区别之一。deque由多个连续的内存块组成,这些内存块之间通过指针或引用相互连接。当在deque的两端插入或删除元素时,只需要调整相应内存块的指针或引用,而不需要像vector那样重新分配整个内存块并复制元素。这使得deque在两端进行插入和删除操作时效率更高。

5. deque的应用场景

deque适用于需要在序列两端进行大量插入和删除操作的情况。例如,在滑动窗口算法中,我们需要维护一个固定大小的窗口,并在窗口滑动时更新窗口中的元素。使用deque可以方便地在窗口的两侧添加和移除元素。此外,deque还可以用于实现循环队列、双端队列等数据结构。

6. deque的性能考虑

虽然deque在两端插入和删除操作上效率很高,但在某些情况下,其性能可能不如vectorlist。例如,当需要在deque的中间频繁地插入或删除元素时,由于deque内部结构的复杂性,这些操作可能会比vectorlist慢。此外,由于deque不保证在内存中连续存储元素,因此它可能无法像vector那样有效地利用缓存。

因此,在选择使用deque时,需要考虑具体的应用场景和性能需求。如果主要操作是在序列的两端进行,那么deque是一个很好的选择。但是,如果需要在序列中间进行大量插入和删除操作,或者需要高效地利用缓存,那么可能需要考虑使用其他容器类型。

7. deque的迭代器失效问题

vectorlist一样,deque的迭代器也可能在容器修改操作后失效。当使用迭代器遍历deque并同时修改它(例如,通过迭代器删除元素)时,需要特别小心。一旦迭代器失效,继续使用它可能会导致未定义的行为。

为了避免迭代器失效问题,可以使用deque的成员函数(如erase())来删除元素,并返回指向下一个有效元素的迭代器。这样,就可以在遍历过程中安全地删除元素。

8. deque的扩展用法

除了基本的插入、删除、访问和修改操作外,deque还可以与其他STL算法结合使用,以实现更复杂的操作。例如,可以使用std::sort()算法对deque中的元素进行排序,或者使用std::unique()算法去除deque中的重复元素。

此外,deque还可以作为其他STL容器的元素类型。例如,可以创建一个包含deque<int>vector,以实现二维动态数组的功能。这种用法在需要灵活处理多维数据结构时非常有用。

9. 示例代码:使用deque实现滑动窗口算法

下面是一个使用deque实现滑动窗口算法的示例代码:

#include <iostream>
#include <deque>
#include <vector>std::vector<int> maxSlidingWindow(std::vector<int>& nums, int k) {std::vector<int> result;std::deque<int> dq;for (int i = 0; i < nums.size(); ++i) {// 维护一个单调递减的deque,存储当前窗口内的最大值索引while (!dq.empty() && nums[dq.back()] < nums[i]) {dq.pop_back();}dq.push_back(i);// 如果deque的头部索引已经超出当前窗口范围,则移除if (dq.front() == i - k) {dq.pop_front();}// 当窗口大小达到k时,将当前窗口的最大值加入结果集if (i >= k - 1) {result.push_back(nums[dq.front()]);}}return result;
}int main() {std::vector<int> nums = {1, 3, -1, -3, 5, 3, 6, 7};int k = 3;std::vector<int> result = maxSlidingWindow(nums, k);for (int val : result) {std::cout << val << " ";}std::cout << std::endl;return 0;
}

这个示例展示了如何使用deque实现一个滑动窗口算法,该算法可以找到给定数组的每个大小为k的子数组中的最大值。通过使用deque来维护一个单调递减的索引队列,我们可以在O(n)的时间复杂度内解决这个问题。

10. deque与其他STL容器的交互

deque可以与其他STL容器(如vector, list, set, map等)进行交互,这主要得益于STL的通用性和容器间的兼容性。例如,你可以将deque中的元素复制到vector中,或者将list中的元素插入到deque中。

下面是一个简单的示例,展示了如何将deque中的元素复制到vector中:

#include <iostream>
#include <deque>
#include <vector>int main() {std::deque<int> intDeque = {1, 2, 3, 4, 5};std::vector<int> intVector(intDeque.begin(), intDeque.end());// 打印vector中的元素for (int val : intVector) {std::cout << val << " ";}std::cout << std::endl;return 0;
}

同样地,你也可以使用std::insert_iteratorstd::back_inserter等迭代器适配器,将deque中的元素插入到list或其他容器中。

11. 自定义deque元素类型

deque可以容纳任何类型的元素,包括自定义类型。你可以定义自己的类或结构体,并将其对象存储在deque中。只需确保自定义类型满足可复制和可赋值的要求,以便在deque中进行元素的插入、删除和移动操作。

下面是一个简单的示例,展示了如何将自定义类型的对象存储在deque中:

#include <iostream>
#include <deque>struct Person {std::string name;int age;Person(const std::string& n, int a) : name(n), age(a) {}// 输出Person对象的信息void print() const {std::cout << "Name: " << name << ", Age: " << age << std::endl;}
};int main() {std::deque<Person> peopleDeque;peopleDeque.emplace_back("Alice", 25);peopleDeque.emplace_back("Bob", 30);// 遍历deque并打印每个人的信息for (const Person& person : peopleDeque) {person.print();}return 0;
}

12. deque的线程安全性

需要注意的是,deque本身并不是线程安全的。如果你在多线程环境中使用deque,并且多个线程同时访问和修改它,那么你需要使用适当的同步机制(如互斥锁、条件变量等)来确保线程安全。

13. deque的替代方案

在某些情况下,你可能需要考虑使用其他容器作为deque的替代方案。例如,如果你需要在序列中间进行大量插入和删除操作,并且不关心元素的顺序,那么list可能是一个更好的选择。list在序列中间插入和删除元素的时间复杂度为O(1),而deque则为O(n)。

另一方面,如果你需要在内存中连续存储元素,并且知道序列的大小在运行时不会改变,那么vector可能是一个更合适的选择。vector在内存中连续存储元素,并且具有更好的缓存利用率和更少的内存碎片。

14. 总结

deque是一个功能强大的STL容器,它支持在序列的两端进行高效的插入和删除操作。通过与其他STL算法和容器的交互,以及自定义元素类型,你可以使用deque来解决各种复杂的问题。然而,在选择使用deque时,你需要考虑具体的应用场景和性能需求,以确保选择最合适的容器类型。

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

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

相关文章

06.docker容器管理

docker的容器管理 创建并且启动一个nginx容器 docker run -d -p 80:80 nginx:latest run&#xff08;创建并运行一个容器&#xff09; -d 放在后台 -p 端口映射 -v 源地址(宿主机):目标地址(容器)创建一个centos系统的容器&#xff0c;并且进入到系统里面 nginx docker镜…

C++大型项目管理:通用CMake框架的架构奥秘

往期本博主的 C 精讲优质博文可通过这篇导航进行查找&#xff1a; Lemo 的C精华博文导航&#xff1a;进阶、精讲、设计模式文章全收录 前言 在当今的软件开发环境中&#xff0c;随着 C 项目的不断扩大和变得越来越复杂&#xff0c;有效地管理这些项目成为了一个重要的挑战。 C…

【WEB前端2024】3D智体编程:乔布斯3D纪念馆-第35课-3D互动教材

【WEB前端2024】3D智体编程&#xff1a;乔布斯3D纪念馆-第35课-3D互动教材 使用dtns.network德塔世界&#xff08;开源的智体世界引擎&#xff09;&#xff0c;策划和设计《乔布斯超大型的开源3D纪念馆》的系列教程。dtns.network是一款主要由JavaScript编写的智体世界引擎&am…

Zero Infinity原理

如上图&#xff0c;一次加载一个layer的一个weights分片&#xff08;一层的1/DP的参数量&#xff09;&#xff0c;Broadcast至所有rank&#xff0c;计算各自的梯度&#xff0c;再Reduce至其中一个负责的rank&#xff0c;offload存放至CPU Memory&#xff0c;释放GPU里的weights…

i.MX8MP平台开发分享(TMU驱动及用户接口篇)

概念 thermal zone 温度控制区域。sensor 获取温度。trip points 温度跳变点&#xff0c;或者是温度阈值。cooling device Thermal Cooling Device 是可以降温设备的抽象&#xff0c;能降温的设备比如风扇&#xff0c;这些好理解&#xff0c;但是像CPU、GPU 这些 Cooling devi…

探究Spring中的Controller:单例、多例及其并发安全性

1. Spring框架的简介 Spring是一个开源的Java平台&#xff0c;用来简化企业级应用程序的开发。Spring框架提供了一整套统一的编程模型&#xff0c;使得开发人员能够更加专注于业务逻辑&#xff0c;而不必去处理复杂的技术细节。Spring包含多个模块&#xff0c;其中最常使用的就…

cesium Material的理解与使用

1.简介 材质Material可以是比较简单的&#xff0c;比如直接将一张图片赋予表面&#xff0c;或者使用条纹状、棋盘状的图案&#xff1b;也可以使用Fabric和GLSL&#xff0c;重新创建一个新的材质或者组合现有的材质。例如&#xff0c;我们可以通过程序生成的纹理(procedural bri…

el-input实现后缀图标和clearable的兼容,调整el-input clearable与自定义图标展示位置问题

背景&#xff1a;常见的输入框存在两个图标的展示效果都是清空在前搜索或其他图标在后 常见以及最终实现效果&#xff08;清空图标在前&#xff0c;搜索图标在后&#xff09; BUG以及el-input默认效果 问题排查 通过控制台审查元素能够发现&#xff0c;默认的效果是自定义图标…

pyautogui模拟鼠标拖动选中文字的基本知识(附Demo)

目录 前言1. Demo1.1 特定窗口点击拖动1.2 屏幕中间点击拖动 2. 基本知识 前言 相关知识推荐阅读&#xff1a; 详细分析Python中的Pyautogui库&#xff08;附Demo&#xff09;详细分析PyAutoGUI中的locate函数&#xff08;附Demo&#xff09; 1. Demo 先给出一部分代码展示…

银河麒麟操作系统安装conda

参考&#xff1a; https://blog.csdn.net/Andy_shenzl/article/details/136294743 查看系统版本 uname -m 下载对应版本 https://mirrors.tuna.tsinghua.edu.cn/anaconda/archive/?CM&OD 安装 bash Anaconda3-2023.09-0-Linux-aarch64.sh 刷新环境 source ~/.bashrc 查看…

基于C#的计算机与安捷伦34970A通信方法

概述 安捷伦34970A采集数据&#xff0c;34970A支持RS232接口&#xff0c;但是如果直接用winform自带的seriaport类基本是没必要使用的&#xff0c;安捷伦等仪表通讯需要用到VISA的库。 库的获取 1. 是德科技的IO Library. 2. NI下载NI-VISA. 两者用法接近. 代码如下 using…

WLAN基础-WLAN安全

目录 一、引言二、WLAN安全威胁三、WLAN安全防御机制四、WLAN常用接入认证方式五、总结 一、引言 随着无线网络的广泛应用&#xff0c;WLAN&#xff08;无线局域网&#xff09;因其灵活性和便利性成为越来越多用户和企业首选的接入方式。然而&#xff0c;由于无线通信开放的传…

【动手学深度学习】多层感知机之暂退法研究详情

目录 &#x1f30a;1. 研究目的 &#x1f30a;2. 研究准备 &#x1f30a;3. 研究内容 &#x1f30d;3.1 多层感知机暂退法 &#x1f30d;3.2 基础练习 &#x1f30a;4. 研究体会 &#x1f30a;1. 研究目的 防止过拟合&#xff1a;权重衰减和暂退法都是用来控制模型的复杂…

基于springboot实现疫情信息管理系统项目【项目源码+论文说明】计算机毕业设计

基于springboot实现疫情信息管理系统演示 摘要 近年来&#xff0c;信息化管理行业的不断兴起&#xff0c;使得人们的日常生活越来越离不开计算机和互联网技术。首先&#xff0c;根据收集到的用户需求分析&#xff0c;对设计系统有一个初步的认识与了解&#xff0c;确定疫情信息…

【C++题解】1392 - 回文偶数?

问题&#xff1a;1392 - 回文偶数&#xff1f; 类型&#xff1a;for循环 题目描述&#xff1a; 小明发现有一类数非常有趣&#xff0c;他们正过来读和反过来读是一样的&#xff0c;比如&#xff1a;121、202、383 等&#xff0c;小明给这类数起了一个名字&#xff0c;叫做回文…

【Python】 将日期转换为 datetime 对象在 Python 中

基本原理 在 Python 中&#xff0c;处理日期和时间的库是 datetime&#xff0c;它提供了广泛的功能来处理日期和时间。datetime 模块中有一个 datetime 类&#xff0c;它可以用来表示日期和时间。有时&#xff0c;我们可能会遇到需要将日期字符串转换为 datetime 对象的情况&a…

《绝区零》测试开启,揭开了米哈游海外战略意图

原标题&#xff1a;《绝区零》公测&#xff0c;米哈游希望用一个成熟平台迎接《绝区零》的诞生 易采游戏网6月5日消息&#xff1a;随着《绝区零》公测日期的逐渐迫近&#xff0c;身为米哈游的忠实拥趸&#xff0c;对于这款新作怀揣着无尽期待。作为中国二次元游戏行业的领军企业…

一文读懂银行承兑汇票:从申请到使用全攻略

银行承兑汇票&#xff08;Banks Acceptance Bill&#xff0c;BA&#xff09;是商业汇票的一种。它是由在承兑银行开立存款账户的存款人出票&#xff0c;向开户银行申请并经银行审查同意承兑的&#xff0c;保证在指定日期无条件支付确定的金额给收款人或持票人的票据。银行承兑汇…

qt c++类继承QWidget和不继承有什么区别

class CheckBoxSetting {Q_OBJECT public:CheckBoxSetting(); };和 class CheckBoxSettingsEditor : public QWidget {Q_OBJECTpublic:explicit CheckBoxSettingsEditor(QWidget *parent 0);~CheckBoxSettingsEditor();有什么区别&#xff1f; 这两个类 CheckBoxSetting 和 C…

Klipper安装

安装必要软件 Klipper相关的软件需要Python等软件才能运行&#xff0c;且需要git来同步最新的Klipper等软件源代码&#xff0c;所以需要使用sudo apt install build-essential python3 python3-pip git来安装必要的软件。 安装好pip后&#xff0c;运行pip config set global.i…