c++中如何使用deque双端队列_c++ deque容器操作实例

c++kquote>std::deque是支持高效两端插入/删除且具备O(1)随机访问的序列容器,声明需#include并指定模板类型,初始化方式包括默认、大小、值、数组及C++11初始化列表。

直接说结论:std::deque 是 C++ 标准库中支持高效两端插入/删除的序列容器,比 std::vector 多出 push_front()pop_front(),但随机访问性能略弱于 vector(常数因子稍大,仍为 O(1))。

如何声明和初始化 deque

必须包含头文件 ,且所有操作都依赖模板参数类型。常见误写是漏掉命名空间或用错尖括号语法。

  • 空容器:std::deque dq;
  • 指定大小并初始化为 0:std::deque dq(5); → 含 5 个 0
  • 指定大小并初始化为某值:std::deque dq(5, 42); 含 5 个 42
  • 从数组构造:int arr[] = {1, 2, 3}; std::deque dq(arr, arr + 3);
  • C++11 起支持初始化列表:std::deque<:string> dq = {"a", "b", "c"};

两端插入与删除的正确写法

deque 的核心价值就在前后操作都是 O(1) 均摊复杂度,但要注意:没有 insert_front() 这种函数,只有 push_front()pop_front()pop_front() 不返回值,取值需先用 front()

  • 前端插入:dq.push_front(10);
  • 后端插入:dq.push_back(20);
  • 前端删除:dq.pop_front();(不返回元素,删前请先 if (!dq.empty()) x = dq.front();
  • 后端删除:dq.pop_back();
  • 获取首尾元素(不删除):dq.front()dq.back() —— 若容器为空,行为未定义(不是抛异常,是崩溃或脏读)
std::deque dq = {1, 2, 3};
dq.push_front(0);   // dq → {0,1,2,3}
dq.push_back(4);    // dq → {0,1,2,3,4}
dq.pop_front();     // dq → {1,2,3,4}
dq.pop_back();      // dq → {1,2,3}

迭代器与随机访问注意事项

deque 支持随机访问(dq[i]dq.at(i)),也支持所有标准迭代器操作,但它的内存不连续 —— 实际由多个固定大小缓冲区组成。这导致两个隐性影响:

立即学习“C++免费学习笔记(深入)”;

  • &dq[0] + i != &dq[i]:指针算术不成立,不能把 dq.data() 当作 C 数组传给需要连续内存的 API(vector 可以,deque 不行)
  • 迭代器失效规则比 vector 宽松:仅在对应端插入/删除时,另一端的迭代器仍有效;但中间插入(insert(pos, ...))会导致所有迭代器失效
  • at() 会做边界检查并抛 std::out_of_rangeoperator[] 不检查 —— 和 vector 行为一致

什么时候该用 deque 而不是 vector 或 list

deque 不是因为“它两头快”,而是因为你的场景同时满足:需要频繁在两端增删 + 需要较快的随机访问 + 不要求内存连续。

  • ✅ 推荐用:滑动窗口最大值实现栈+队列混合结构日志缓冲区(头进尾出或尾进头出)
  • ❌ 别硬套:需要传给 OpenGL 的顶点数组(要 vector::data())、99% 时间只在尾部操作(用 vector 更缓存友好)、频繁在中间插入/删除(改用 listforward_list
  • ⚠️ 注意:deque 构造/析构单个元素的开销略高于 vector(因涉及缓冲区管理),元素类型很重时(如含大数组的 struct)要实测

最容易被忽略的一点:dequesize() 是 O(1),但某些老编译器(如早期 MSVC)实现曾是 O(n),现在主流 STL(libstdc++、libc++、MSVC STL)均已保证 O(1)。不过,如果你在嵌入式环境或自研 STL 上使用,务必查证文档。