Deque
STL deque 简介
std::deque
(双端队列)是 STL 提供的一种序列式容器。它是一种 允许在序列的两端(前端和后端)进行快速插入和删除操作 的数据结构。与只能在一端进行操作的 std::queue
和 std::stack
不同,deque
提供了更大的灵活性。此外,deque
还 支持随机访问,就像数组和 std::vector
一样,允许你通过索引直接访问任何位置的元素。
源文件 指出,deque
结合了 vector
和 list
的一些优点。像 vector
一样,它支持随机访问,但与 vector
在前端进行插入和删除操作性能较差不同,deque
在两端进行这些操作都非常高效。这与 list
的特性类似,因为 list
(STL 中的 std::list
)是使用双向链表实现的,也支持快速的两端插入和删除。
std::deque
的实现方式
一个非常关键的点是,STL 的 std::deque
并不是简单的作为双向链表来实现的。虽然概念上双向链表可以支持两端的快速插入和删除,但 STL deque
的实现更加复杂,旨在提供 常数时间复杂度的随机访问(摊销) 以及 常数时间复杂度的两端插入和删除操作。
源文件 解释说,std::deque
通常被实现为由多个连续的内存块(或者说缓冲区)组成的结构。为了管理这些不连续的内存块,deque
内部维护了一个 索引数组(或者是指针数组),该数组存储了指向每个内存块起始地址的指针。
当需要在 deque
的前端或后端插入新的元素而某个内存块已满时,deque
会分配一个新的内存块,并更新其内部的索引数组,使其指向新的内存块。当访问 deque
中的某个元素时,deque
会首先通过索引计算出该元素所在的内存块,然后再在该内存块中定位到具体的元素,从而实现随机访问。
这种分段式的实现方式使得 deque
在两端进行插入和删除操作时,只需要在相应的内存块中进行操作,并在必要时分配新的内存块和更新索引,避免了像 vector
那样可能发生的整体数据搬移。同时,通过维护索引数组,deque
也实现了高效的随机访问。
简单总结
std::deque
是 STL 中的一个 双端队列容器,支持在 前端和后端进行高效的插入和删除操作。std::deque
支持随机访问,类似于数组和std::vector
。- 在实现上,
std::deque
通常不是一个简单的链表,而是由多个连续的内存块和一个索引数组构成,这使得它能够在提供两端高效操作的同时,也支持高效的随机访问。 std::queue
默认就是基于std::deque
实现的,但queue
只暴露了队列的 FIFO 行为,限制了对deque
两端操作和随机访问的直接使用 [我们的对话]。
因此,std::deque
是一种非常灵活的数据结构,适用于既需要在两端进行频繁操作,又需要能够随机访问元素的场景。
STL deque
常用成员函数
成员函数 | 操作 |
---|---|
assign(iterator first, iterator last) |
移除 deque 中的所有元素,并插入由迭代器 first 和 last 指示的范围内的元素。 |
assign(size_type n, const T& el = T()) |
移除 deque 中的所有元素,并插入 n 个 el 的副本。如果未提供 el ,则使用 T() 默认构造函数。 |
at(size_type n) |
返回 deque 中位置 n 的元素的引用(提供边界检查)。 |
const at(size_type n) const |
返回 deque 中位置 n 的元素的常量引用(提供边界检查)。 |
back() |
返回 deque 中最后一个元素的引用。 |
const back() const |
返回 deque 中最后一个元素的常量引用。 |
begin() |
返回一个指向 deque 第一个元素的迭代器。 |
const_iterator begin() const |
返回一个指向 deque 第一个元素的常量迭代器。 |
clear() |
移除 deque 中的所有元素。 |
deque() |
构造一个空的 deque 。 |
deque(size_type n, const T& el = T()) |
构造一个包含 n 个 el 副本的 deque 。 |
deque(const deque<T>& dq) |
拷贝构造函数,创建一个 dq 的副本。 |
deque(iterator first, iterator last) |
构造一个 deque 并使用由迭代器 first 和 last 指示的范围内的值进行初始化。 |
empty() const |
如果 deque 不包含任何元素,则返回 true ,否则返回 false 。 |
end() |
返回一个指向 deque 最后一个元素之后位置的迭代器。 |
const_iterator end() const |
返回一个指向 deque 最后一个元素之后位置的常量迭代器。 |
erase(iterator i) |
移除迭代器 i 所引用的元素,并返回一个指向被移除元素之后位置的迭代器。 |
erase(iterator first, iterator last) |
移除由迭代器 first 和 last 指示范围内的元素,并返回一个指向被移除的最后一个元素之后位置的迭代器。 |
front() |
返回 deque 中第一个元素的引用。 |
const front() const |
返回 deque 中第一个元素的常量引用。 |
insert(iterator i, const T& el = T()) |
在迭代器 i 所指示的元素之前插入元素 el ,并返回一个指向新插入元素的迭代器。 |
insert(iterator i, size_type n, const T& el) |
在迭代器 i 所指示的元素之前插入 n 个 el 的副本。 |
insert(iterator i, iterator first, iterator last) |
在迭代器 i 所指示的元素之前插入由迭代器 first 到 last (不包括 last )指示范围内的元素。 |
max_size() const |
返回 deque 可以容纳的最大元素个数。 |
operator[] |
下标运算符,返回 deque 中指定位置的元素的引用(不提供边界检查)。 |
pop_back() |
移除 deque 的最后一个元素。 |
pop_front() |
移除 deque 的第一个元素。 |
push_back(const T& el) |
在 deque 的末尾 插入元素 el 。 |
push_front(const T& el) |
在 deque 的开头 插入元素 el 。 |
rbegin() |
返回一个指向 deque 最后一个元素的逆向迭代器。 |
const_reverse_iterator rbegin() const |
返回一个指向 deque 最后一个元素的常量逆向迭代器。 |
rend() |
返回一个指向 deque 第一个元素之前位置的逆向迭代器。 |
const_reverse_iterator rend() const |
返回一个指向 deque 第一个元素之前位置的常量逆向迭代器。 |
resize(size_type n, const T& el = T()) |
改变 deque 的大小为 n 。如果 n 大于当前大小,则在末尾添加值为 el 的新元素;如果 n 小于当前大小,则从末尾移除多余的元素。 |
size() const |
返回 deque 中当前元素的个数。 |
swap(deque<T>& dq) |
交换 deque 的内容与另一个 deque dq 的内容。 |
deque
演示程序
#include <iostream>
#include <algorithm>
#include <deque>
using namespace std;
int main() {
deque<int> dq1;
dq1.push_front(1); // dq1 = (1)
dq1.push_front(2); // dq1 = (2 1)
dq1.push_back(3); // dq1 = (2 1 3)
dq1.push_back(4); // dq1 = (2 1 3 4)
deque<int> dq2(dq1.begin() + 1, dq1.end() - 1); // dq2 = (1 3)
dq1 = 5; // dq1 = (2 5 3 4)
dq1.erase(dq1.begin()); // dq1 = (5 3 4)
dq1.insert(dq1.end() - 1, 2, 6); // dq1 = (5 3 6 6 4)
sort(dq1.begin(), dq1.end()); // dq1 = (3 4 5 6 6)
deque<int> dq3;
dq3.resize(dq1.size() + dq2.size()); // dq3 = (0 0 0 0 0 0 0)
merge(dq1.begin(), dq1.end(), dq2.begin(), dq2.end(), dq3.begin());
// dq1 = (3 4 5 6 6) and dq2 = (1 3) ==> dq3 = (1 3 3 4 5 6 6)
cout << "dq1 的元素: ";
for (int val : dq1) {
cout << val << " ";
}
cout << endl; // 输出: dq1 的元素: 3 4 5 6 6
cout << "dq2 的元素: ";
for (int val : dq2) {
cout << val << " ";
}
cout << endl; // 输出: dq2 的元素: 1 3
cout << "dq3 的元素: ";
for (int val : dq3) {
cout << val << " ";
}
cout << endl; // 输出: dq3 的元素: 1 3 3 4 5 6 6
return 0;
}
程序说明:
- 程序首先包含了必要的头文件
<iostream>
,<algorithm>
和<deque>
. - 创建了一个
deque<int>
对象dq1
,并演示了push_front()
(在前端插入)、push_back()
(在后端插入) 的使用。 - 创建了第二个
deque<int>
对象dq2
,并使用dq1
的一部分元素进行初始化(使用迭代器指定范围)。 - 演示了使用下标运算符
[]
修改dq1
中间位置的元素。 - 使用
erase()
移除dq1
的第一个元素。 - 使用
insert()
在dq1
倒数第二个位置插入两个值为 6 的元素。 - 使用
sort()
算法对dq1
中的元素进行排序。 - 创建了第三个
deque<int>
对象dq3
,并使用resize()
改变了其大小。 - 使用
merge()
算法将排序后的dq1
和dq2
的元素合并到dq3
中(需要包含<algorithm>
头文件)。 - 最后,程序通过循环输出了
dq1
、dq2
和dq3
中的元素,展示了上述操作的结果。
这个程序演示了 std::deque
的一些常用成员函数,包括在两端插入和删除元素、使用迭代器、随机访问以及与其他 STL 算法的配合使用。同时也印证了 deque
具有类似于 vector
的随机访问能力以及类似于 list
的两端高效插入删除能力。