【C++学习】deque容器
一、deque 的核心特性
deque 是 STL 中 “全能型” 容器,核心价值是兼顾双端高效操作和随机访问,尤其适合需要频繁在首尾增删元素,同时又需通过索引访问的场景。实际开发中,若不确定用 vector 还是 list,且有双端操作需求,deque 是优先选择。
双端高效操作
首尾插入(push_front/push_back)、首尾删除(pop_front/pop_back)的时间复杂度均为 O(1),这是它区别于 vector(仅尾端高效)和 list(随机访问低效)的核心优势;
随机访问支持
可通过索引([]/at())访问元素,时间复杂度 O(1),但效率略低于 vector(底层结构更复杂);
动态扩容
无需提前固定大小,扩容时不移动全部元素(与 vector 不同),内存利用率更高;
底层结构
并非完全连续内存,而是由「多个连续内存块 + 中控数组」组成 —— 中控数组存储各内存块的地址,元素访问时先通过中控数组定位内存块,再访问块内元素,兼顾了双端操作和随机访问效率;
模板实现:支持存储任意类型(int、string、自定义类等),定义时需指定元素类型(如 deque)。
二、deque 的常用操作
1. 初始化(5 种常见方式)
#include <deque>
#include <iostream>
using namespace std;int main() {// 1. 空deque(默认初始化)deque<int> d1;// 2. 初始化n个默认值元素(int默认0,string默认空串)deque<int> d2(5); // 5个0:[0,0,0,0,0]deque<string> d3(3); // 3个空串:["","",""]// 3. 初始化n个指定值元素deque<int> d4(4, 10); // 4个10:[10,10,10,10]// 4. 用初始化列表赋值(C++11+)deque<int> d5 = {1, 2, 3, 4}; // [1,2,3,4]deque<int> d6{5, 6, 7}; // 等价于d5的写法// 5. 拷贝其他deque(深拷贝)deque<int> d7(d5); // 拷贝d5:[1,2,3,4]deque<int> d8 = d6; // 拷贝d6:[5,6,7]return 0;
}
2.元素访问(4 种方式,与 vector 类似)
int main() {deque<int> d = {10, 20, 30, 40};// 1. 索引访问([]):无越界检查,越界会崩溃cout << d[0] << endl; // 10cout << d[2] << endl; // 30// 2. at() 访问:有越界检查,越界抛异常(更安全)cout << d.at(1) << endl; // 20// d.at(10); // 抛出 out_of_range 异常// 3. 访问首尾元素(核心优势,高效)cout << d.front() << endl; // 首元素:10cout << d.back() << endl; // 尾元素:40// 4. 迭代器/范围for遍历for (deque<int>::iterator it = d.begin(); it != d.end(); ++it) {cout << *it << " "; // 10 20 30 40}for (int x : d) {cout << x << " "; // 10 20 30 40}return 0;
}
3. 元素修改(双端操作是核心)
int main() {deque<int> d;// 1. 双端插入(核心功能,O(1))d.push_back(10); // 尾插:[10]d.push_back(20); // 尾插:[10,20]d.push_front(5); // 首插:[5,10,20]d.push_front(1); // 首插:[1,5,10,20]// 2. 双端删除(核心功能,O(1))d.pop_back(); // 尾删:[1,5,10]d.pop_front(); // 首删:[5,10]// 3. 中间插入/删除(效率低于 list,O(n))// 在索引1处插入3(迭代器定位)d.insert(d.begin() + 1, 3); // [5,3,10]// 删除索引0的元素d.erase(d.begin()); // [3,10]// 4. 清空元素(clear):size变为0,容量可能保留d.clear(); // 空dequereturn 0;
}
4. 容量与大小操作
int main() {deque<int> d(4, 10); // 初始:size=4// 1. 获取当前元素个数(size)cout << "size: " << d.size() << endl; // 4// 2. 检查是否为空(empty)cout << "empty: " << (d.empty() ? "是" : "否") << endl; // 否// 3. 调整大小(resize):新增元素补默认值/指定值d.resize(6); // size=6,新增2个0:[10,10,10,10,0,0]d.resize(8, 5); // size=8,新增2个5:[10,10,10,10,0,0,5,5]// 4. 预留容量(reserve):deque的reserve仅保证容量不小于n,不收缩容量d.reserve(10); // 容量至少为10,size仍为8return 0;
}
三、deque 的常见使用场景
双端队列需求
如实现 “滑动窗口” 算法(需从首尾删除元素)、缓存队列(需从队首取元素,队尾加元素);
作为容器适配器的底层
STL 中的 stack(栈)和 queue(队列)默认用 deque 作为底层容器(因双端操作高效,且支持随机访问);
替代 vector 的首插场景
若需频繁在头部插入元素,deque 的 push_front(O (1))远优于 vector 的首插(O (n))。
四、注意事项
迭代器失效问题
deque 的首尾插入 / 删除可能导致迭代器失效(若触发内存块增减),中间插入 / 删除会导致插入点后的迭代器失效;
避免在迭代器访问期间修改 deque 大小(如 push_back/pop_front),若修改需重新获取迭代器。
内存碎片
虽 deque 扩容不移动全部元素,但多个内存块可能导致一定内存碎片,若对内存连续性要求极高(如直接访问内存地址),需用 vector。
自定义类存储
与 vector 类似,存储自定义类时需确保有默认构造函数,若包含动态内存,需实现深拷贝(避免内存泄漏)。
