【C++】STL的百宝箱—全能deque的简单讲解

【C++】STL的百宝箱—全能deque的简单讲解
✨ 坚持用清晰易懂的图解+代码语言, 让每个知识点都简单直观!
🚀 个人主页不呆头 · ZEEKLOG
🌱 代码仓库不呆头 · Gitee
📌 专栏系列 :📖 《C语言》🧩 《数据结构》💡 《C++》🐧 《Linux》💬 座右铭 :“不患无位,患所以立。”
在这里插入图片描述

【C++】STL的百宝箱—全能deque讲解


摘要

在 STL 的容器家族里,deque(双端队列)就像一个“全能型选手”。它既能像 vector 一样支持随机访问,又能像 list 一样在两端高效插入删除。虽然它并不是完美的存在,但凭借灵活的底层结构和均衡的性能,deque 成为了 stack 和 queue 的首选底层容器。本文将带你全面认识 deque,从设计动机到底层实现,再到应用场景,一网打尽。

📌 编程箴言:
“好的C++代码就像好酒,需要时间沉淀。”


目录

一、deque的简单介绍

1. 为什么需要deque?

我们知道,vector 的优点是能够随机访问下标,尾插尾删效率高,且 CPU 缓存命中率高;但它的不足在于头插头删效率低下,因为插入或删除时需要整体数据前移或后移,复杂度为 O(n),并且在内存不足时需要扩容,可能造成空间浪费。

list 的优点是按需分配空间,不存在额外浪费,并且能够在任意位置进行插入和删除,复杂度为 O(1);但缺点是当空间不足时需要频繁申请内存,且不支持下标随机访问。

因此,人们希望同时结合二者的优点、互补缺点,于是便创造出了 deque


2. 了解deque

在这里插入图片描述


在这里插入图片描述


std::deque(double-ended queue,双端队列)是 C++ STL 提供的一种 序列式容器,它和 vector 有点像,都能动态扩容并支持随机访问。但它的最大特点是:可以在容器的头尾两端高效地插入和删除元素,而不仅仅是尾部。你可以把它想象成一个“带前后两个门的房间”,不管是从前门塞进去,还是从后门塞进去,都很方便。因为底层采用分段数组(而不是连续内存),所以插入删除更灵活,但随机访问速度略逊于 vector。与list比较,空间利用率比较高,不需要存储额外字段。

在这里插入图片描述


但deque并不是真正的连续空间(空间连续了但没完全连续),而是由一段段连续的小空间拼接而成的,实际deque类似于一个动态的二维数组,其底层结构如下图所示:

在这里插入图片描述
  • 入图所示,我们在buffer1的位置满了之后需要头插数据的话,就开一个buffer0,在buffer0的最后一个空间插入7;同理buffer1的位置满了之后需要尾插一个数据的话,就开一个buffer2,在buffer2的第一个空间开始插入8。
  • 我们用一个指针数组维护这块空间,例如buffer1存在这块数组的中间,当空间满了之后就可以向前(buffer0)以及向后扩容(buffer1)。

二、deque的迭代器

当然为了能够支持随机访问,deque的iterator迭代器就设计的十分复杂了deque的迭代器封装了四个指针,first和last用来指向buffer的头尾,cur用来遍历buffer,然后将node指向中控数组对应位置:

在这里插入图片描述


同时还有指向头尾buffer的start和first:

在这里插入图片描述


map是一个二级指针,因为中控数组是一个指针数组,而map存放的中控数组的地址。


三、为什么选择 deque?

  1. 对比 vector
    • deque 在头部插入和删除时不需要整体搬移元素,效率远高于 vector
    • 扩容时,deque 只需新开 buffer,并在必要时扩容中控数组,而不像 vector 那样搬移大量元素,因此效率更高。
  2. 对比 list
    • deque 的底层是分段连续空间,整体空间利用率高,不需要像 list 那样为每个节点存储额外的 prevnext 指针。
  3. 与 stack / queue 的关系
    • stack 是后进先出结构,只需要 push_backpop_back 就能实现,因此 vectorlistdeque 都能作为其底层容器。
    • queue 需要一端插入、另一端删除,deque 的双端操作正好契合。
    • stackqueue 本身不需要迭代器,因此完全避开了 deque 在遍历上的劣势。
  4. 效率与内存
    • stack 中,deque 的扩容效率比 vector 高(无需整体搬移)。
    • queue 中,deque 既保证了高效扩容,又具有良好的缓存命中率和内存利用率,不易产生大量内存碎片。

总结
deque 结合了 vectorlist 的优点:既支持双端高效操作,又具备较好的空间局部性和扩容效率。因此,作为 stackqueue 的底层容器,是一个更优的选择。


四、为什么deque无法完全替代vector和list?

vector的随机访问和list的任意位置插入删除O(1)的时间复杂度做到了极致,而deque的中间位置的插入删除和随机访问的效率都差强人意,所以deque是无法替代vector和list的.而且deque有一个致命的缺点:不适合遍历,因为在遍历时,deque的迭代器要频繁的去检测其是否移动到某段小空间的边界,导致效率低下,还不如直接拷贝到vector中再遍历,但是对stack和queue来说恰好是不需要遍历,所以STL将deque用其作为stack和queue的底层数据结构

五、deque的简单使用

voidtest_deque(){ deque<int> dq; dq.push_back(1); dq.push_back(2); dq.push_back(3); dq.push_back(4); deque<int>::iterator it = dq.begin();while(it != dq.end()){ cout <<*it <<' '; it++;} cout << endl;}

结尾总结

deque 并不是为了替代 vectorlist 而存在,而是作为一种 折中与平衡 的选择:

  • 它弥补了 vector 头部操作的低效,
  • 也避免了 list 节点式存储带来的空间开销,
  • 并凭借双端操作的优势,成为 stackqueue 的最佳搭档。

所以,理解 deque,不仅能帮助我们选对容器,还能更好地体会 STL 设计中“权衡与取舍”的智慧。


不是呆头将一直坚持用清晰易懂的图解+代码语言,让每个知识点变得简单!
👁️ 【关注】 看一个非典型程序员如何用野路子解决正经问题
👍 【点赞】 给“不写八股文”的技术分享一点鼓励
🔖 【收藏】 把这些“奇怪但有用”的代码技巧打包带走
💬 【评论】 来聊聊——你遇到过最“呆头”的 Bug 是啥?
🗳️ 【投票】 您的投票是支持我前行的动力
技术没有标准答案,让我们一起用最有趣的方式,写出最靠谱的代码! 🎮💻

Read more

【架构心法】告别 switch-case 梦魇:基于现代 C++ (std::variant / std::visit) 构筑绝对类型安全的有限状态机 (FSM)

摘要:在复杂的机器人与嵌入式控制逻辑中,基于枚举和条件分支的传统状态机不仅代码极度臃肿,且极易因为漏写 break 或未处理某个特定状态而引发毁灭性的灾难。本文将抛弃面向过程的妥协,深度解构现代 C++17 的高级特性。我们将状态抽象为独立的“类型 (Type)”,利用 std::variant 实现内存的零开销复用,并通过 std::visit 强迫编译器为你检查状态遗漏,带你领略“编译通过即运行正确”的极致架构美学。 一、 传统 FSM 的“三宗罪” 几乎所有的 C/C++ 程序员都写过这样的状态机: enum class RobotState { IDLE, CALIBRATING, MOVING, ERROR }; RobotState currentState = RobotState::IDLE; // 灾难的温床 void updateLogic() { switch (currentState)

By Ne0inhk
极简即王道 下一代Agent架构Pi Agent Core设计逻辑深度解析

极简即王道 下一代Agent架构Pi Agent Core设计逻辑深度解析

在当前人工智能Agent领域的发展浪潮中,各类框架层出不穷,大多数开发者都陷入了一种“加法思维”的误区,认为Agent的能力提升必然依赖更多的工具、更长的提示词、更复杂的规划链路以及更多的子Agent。然而,由Mario Zechner开发的Pi Agent Core(以下简称Pi)却走出了一条截然不同的道路,它以“极简主义”为核心哲学,用不到1500行代码、5个核心文件,在Terminal-Bench 2.0排行榜中与众多复杂架构的Agent同台竞技并跻身前列,重新定义了下一代Agent的设计逻辑。 Pi的核心哲学源自Mario Zechner的一句总结,An autonomous agent is just an LLM + tools + a loop. 这句话看似简单,却直击Agent的本质,也成为了Pi所有设计决策的出发点。作为一名在Agent开发领域有着深刻经验的开发者,Mario Zechner在长期实践中发现,当前很多Agent框架的复杂设计不仅没有提升效率,反而增加了系统的冗余度和维护成本,甚至影响了Agent的自主性和灵活性。于是,他摒弃了主流的加法思路,选择用减

By Ne0inhk
Flutter 组件 censor_it 适配鸿蒙 HarmonyOS 实战:离线内容净化墙,构建端侧敏感词过滤与合规性治理架构

Flutter 组件 censor_it 适配鸿蒙 HarmonyOS 实战:离线内容净化墙,构建端侧敏感词过滤与合规性治理架构

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.ZEEKLOG.net Flutter 组件 censor_it 适配鸿蒙 HarmonyOS 实战:离线内容净化墙,构建端侧敏感词过滤与合规性治理架构 前言 在鸿蒙(OpenHarmony)生态迈向内容社交、即时通讯及 UGC(用户生成内容)全场景覆盖的背景下,如何确保信息的合规性、在端侧拦截违规内容,已成为提升应用生态安全性与用户粘性的“风控红线”。在鸿蒙设备这类强调分布式隐私与绿色上网环境的终端上,如果内容过滤完全依赖云端接口,不仅会由于由于网络往返导致明显的交互滞后,更会由于由于频繁的 API 调用增加额外的运营成本。 我们需要一种能够在端侧执行高速扫描、支持动态字典更新且具备算法透明性的字符过滤引擎。 censor_it 为 Flutter 开发者引入了轻量级的敏感词过滤方案。它通过高效的字符串匹配算法,自动将预设的敏感源转化为可定制的和谐占位符。在适配到鸿蒙 HarmonyOS 流程中,这一组件能够作为鸿蒙应用内容发布的“安检门”,通过在前置环节对文本执行离线脱敏处理

By Ne0inhk

OpenClaw.ai:Agentic AI 时代的“SpringFramework”时刻

—— 关于下一代智能体基础设施架构、生态演进与企业级可行性的系统性研究报告 第一章 历史的镜像:从软件危机到 Agentic AI 的基础设施真空 1.1 J2EE 的黄昏与 Spring 的黎明:关于复杂性的辩证法 要理解“Spring Framework 时刻”的深刻含义,我们必须将目光投向 21 世纪初的 Java 企业级开发领域。彼时,J2EE(Java 2 Platform, Enterprise Edition)虽然承诺了分布式计算的宏大愿景,但其实现方式——特别是 EJB(Enterprise JavaBeans)——却陷入了过度设计的泥潭。开发者被迫编写大量的 XML 配置文件,继承复杂的接口,不仅难以进行单元测试,且组件之间的耦合度极高。这种“重量级”框架导致的开发效率低下,被称为“J2EE

By Ne0inhk