双端队列deque
std::deque
(双端队列,Double-ended Queue)是 C++ 标准模板库(STL)中的一种序列容器,允许在两端(前端和后端)高效地插入和删除元素。deque
提供了与 std::vector
类似的功能,但 deque
支持在容器的头部和尾部同时进行高效的插入和删除操作,而 vector
在头部插入元素时效率较低,因为它需要移动所有元素。
1. std::deque
的特点
- 动态大小:与
std::vector
类似,std::deque
的大小可以动态调整。 - 高效的双端操作:
deque
支持在头部和尾部进行常数时间(O(1))的插入和删除操作。 - 随机访问:与
std::vector
一样,deque
支持常数时间(O(1))的随机访问。 - 灵活的内存管理:
deque
不是像vector
那样的连续内存块,它的底层实现通常由多个连续的小块内存组成,因此它的内存分配更加灵活。
2. std::deque
的用法
2.1 初始化 std::deque
std::deque
支持多种初始化方式:
#include <iostream>
#include <deque>
int main() {
// 默认构造空双端队列
std::deque<int> dq1;
// 使用初始值构造
std::deque<int> dq2(5, 10); // 5 个元素,值为 10
// 使用列表初始化
std::deque<int> dq3 = {1, 2, 3, 4, 5};
// 拷贝构造
std::deque<int> dq4(dq3);
// 输出 deque 的内容
for (int n : dq3) {
std::cout << n << " "; // 输出: 1 2 3 4 5
}
return 0;
}
2.2 插入和删除操作
std::deque
提供了多种操作来支持在头部和尾部插入和删除元素。
6. 适用场景
5. std::deque
与 std::vector
的区别
push_back()
:在末尾插入元素。push_front()
:在头部插入元素。pop_back()
:删除末尾元素。pop_front()
:删除头部元素。#include <iostream> #include <deque> int main() { std::deque<int> dq; // 在队列末尾添加元素 dq.push_back(10); dq.push_back(20); // 在队列头部添加元素 dq.push_front(30); dq.push_front(40); // 输出 deque 的内容 for (int n : dq) { std::cout << n << " "; // 输出: 40 30 10 20 } // 删除头部和尾部的元素 dq.pop_front(); dq.pop_back(); std::cout << "\nAfter pop operations: "; for (int n : dq) { std::cout << n << " "; // 输出: 30 10 } return 0; }
2.3 随机访问
与
std::vector
类似,deque
也支持常数时间的随机访问,使用下标运算符[]
或at()
来访问特定位置的元素。#include <iostream> #include <deque> int main() { std::deque<int> dq = {10, 20, 30, 40}; // 使用下标运算符访问元素 std::cout << "Element at index 1: " << dq[1] << std::endl; // 输出: 20 // 使用 at() 访问元素 std::cout << "Element at index 3: " << dq.at(3) << std::endl; // 输出: 40 return 0; }
2.4 大小与容量操作
std::deque
提供了一些方法来检查容器的大小、是否为空等。size()
:返回容器中的元素数量。empty()
:检查容器是否为空。resize()
:调整容器大小。#include <iostream> #include <deque> int main() { std::deque<int> dq = {1, 2, 3}; std::cout << "Size: " << dq.size() << std::endl; // 输出: 3 // 调整大小 dq.resize(5, 10); // 将大小调整为 5,新增元素值为 10 std::cout << "After resizing: "; for (int n : dq) { std::cout << n << " "; // 输出: 1 2 3 10 10 } return 0; }
3. 高级操作
3.1 插入与删除
std::deque
允许在任意位置插入或删除元素,使用insert()
和erase()
函数。#include <iostream> #include <deque> int main() { std::deque<int> dq = {1, 2, 3, 4, 5}; // 在第三个位置插入元素 dq.insert(dq.begin() + 2, 100); // {1, 2, 100, 3, 4, 5} // 删除第三个位置的元素 dq.erase(dq.begin() + 2); // {1, 2, 3, 4, 5} // 删除范围内的元素 dq.erase(dq.begin(), dq.begin() + 2); // {3, 4, 5} for (int n : dq) { std::cout << n << " "; // 输出: 3 4 5 } return 0; }
3.2 迭代器支持
std::deque
支持迭代器,可以使用标准的迭代器操作来遍历deque
。此外,deque
还支持反向迭代器。#include <iostream> #include <deque> int main() { std::deque<int> dq = {10, 20, 30, 40}; // 正向遍历 std::cout << "Forward iteration: "; for (auto it = dq.begin(); it != dq.end(); ++it) { std::cout << *it << " "; // 输出: 10 20 30 40 } // 反向遍历 std::cout << "\nReverse iteration: "; for (auto rit = dq.rbegin(); rit != dq.rend(); ++rit) { std::cout << *rit << " "; // 输出: 40 30 20 10 } return 0; }
3.3 双端队列与
std::queue
适配器std::deque
是std::queue
容器适配器的默认底层容器。通过std::queue
,可以利用deque
的双端性质来实现标准队列行为。#include <iostream> #include <deque> #include <queue> int main() { std::queue<int> q; q.push(1); // 插入元素 q.push(2); q.push(3); std::cout << "Front element: " << q.front() << std::endl; // 输出: 1 std::cout << "Back element: " << q.back() << std::endl; // 输出: 3 q.pop(); // 移除前端元素 std::cout << "After pop, front element: " << q.front() << std::endl; // 输出: 2 return 0; }
4.
std::deque
的效率与实现std::deque
在头部和尾部的插入和删除操作的效率是 O(1) 的,因为它不需要像vector
那样进行大量的数据移动。- 由于
deque
并不是一个连续的内存块,它由一系列小的内存块(称为缓冲区)组成,因此在内存管理上更灵活,适合频繁在两端进行插入和删除的操作。 - 随机访问
deque
中的元素是 O(1) 的,这使得它与vector
类似,可以进行高效的下标访问。 - 内存结构:
std::vector
是一个连续的内存块,而std::deque
则是由多个小内存块组成。 - 插入和删除操作:
std::deque
支持在头部和尾部进行高效的插入和删除操作,而std::vector
只在尾部具有这种高效性,头部插入会导致所有元素移动。 - 内存重分配:
std::vector
在内存不足时需要重分配整个数组,而std::deque
可以通过动态增加内存块来扩展容量,因此减少了大规模的内存拷贝。 - 需要双端操作:如果你需要在容器的头部和尾部都进行频繁的插入或删除操作,
std::deque
是一个很好的选择。 - 需要随机访问:如果需要支持随机访问并且需要双端操作,
std::deque
也是一个不错的选择。 - 不适合:如果只需要在末尾插入或删除大量元素并进行连续的访问,
std::vector
可能是更好的选择,因为它在尾部操作上的性能与deque
相当,但在空间利用率上更为紧凑。