deque 是 double-ended queue 的缩写,又称双端队列容器。
deque 容器和 vecotr 容器有很多相似之处,比如:
deque 容器也擅长在序列尾部添加或删除元素(时间复杂度为O(1)),而不擅长在序列中间添加或删除元素。
deque 容器也可以根据需要修改自身的容量和大小。
和 vector 不同的是,deque 还擅长在序列头部添加或删除元素,所耗费的时间复杂度也为常数阶O(1)。当需要向序列两端频繁的添加或删除元素时,应首选 deque
容器。
deque 容器以模板类 deque(T 为存储元素的类型)的形式在 头文件中,并位于 std 命名空间中。因此,在使用该容器之前,代码中需要包含下面两行
代码:
#include <deque>
using namespace std;
创建 deque 容器,根据不同的实际场景,可选择使用如下几种方式
- 创建一个没有任何元素的空 deque 容器:
deque<int> d;
和空 array 容器不同,空的 deque 容器在创建之后可以做添加或删除元素的操作,因此这种简单创建 deque 容器的方式比较常见。
- 创建一个具有 n 个元素的 deque 容器,其中每个元素都采用对应类型的默认值:
deque<int> d(10);
此行代码创建一个具有 10 个元素(默认都为 0)的 deque 容器。
- 创建一个具有 n 个元素的 deque 容器,并为每个元素都指定初始值,例如:
deque<int> d(10, 5);
如此就创建了一个包含 10 个元素(值都为 5)的 deque 容器。
- 在已有 deque 容器的情况下,可以通过拷贝该容器创建一个新的 deque 容器,例如:
deque<int> d1(5);
deque<int> d2(d1);
注意,采用此方式,必须保证新旧容器存储的元素类型一致。
- 通过拷贝其他类型容器中指定区域内的元素(也可以是普通数组),可以创建一个新容器,例如:
//拷贝普通数组,创建deque容器
int a[] = { 1,2,3,4,5 };
deque<int>d(a, a + 5);
//适用于所有类型的容器
array<int, 5>arr{ 11,12,13,14,15 };
deque<int>d(arr.begin()+2, arr.end());//拷贝arr容器中的{13,14,15}
deque 容器中,无论是添加元素还是删除元素,都只能借助 deque 模板类提供的成员函数:
成员函数 | 功能
push_back( ) | 队尾插入元素
push_front( ) | 队首插入元素
pop_back( ) | 队尾删除元素
pop_front( ) | 队首删除元素
emplace_back( ) | 队尾生成元素(C++11添加)
emplace_front( ) | 队首生成元素(C++11添加)
insert( ) | 在指定位置生成元素
emplace( ) | 在指定位置生成元素(C++11添加),比insert( )省时
erase( ) | 删除指定位置或区域的元素
clear( ) | 删除元素中所有元素
重点讲一下 insert( ) 函数的用法。insert( ) 函数的功能是在 deque 容器的指定位置插入一个或多个元素。该函数的语法格式有多种,如下所示:
insert(pos,elem) 在pos前插入elem
insert(pos,n,elem) 在pos前插入n个elem
insert(pos,first,last) 在pos前插入其他容器[first,last)间的所有元素
insert(pos,initlist) 在pos之前插入initlist的元素,initlist是大括号括起的多个元素,中间用逗号相连
容器适配器,其就是将不适用的序列式容器(包括 vector、deque 和 list)变得适用。容器适配器本质上还是容器,只不过此容器模板类的实现,利用了大量其它基础容器模板类中已经写好的成员函数。当然,如果必要的话,容器适配器中也可以自创新的成员函数。
STL 提供了 3 种容器适配器,分别为 stack 栈适配器、queue 队列适配器以及 priority_queue 优先权队列适配器。
由于 stack 适配器以模板类
stack<T,Container=deque<T> >
其中 T 为存储元素的类型,Container 表示底层容器的类型)的形式位于头文件中,并定义在 std 命名空间里。因此,在创建该容器之前,程序中应包含以下 2 行代码:
#include <stack>
using namespace std;
创建 stack 适配器,大致分为如下几种方式。
- 创建一个不包含任何元素的 stack 适配器,并采用默认的 deque 基础容器:
stack<int> values;
上面这行代码,就成功创建了一个可存储 int 类型元素,底层采用 deque 基础容器的 stack 适配器。
- 上面提到,
stack<T,Container=deque<T> >
模板类提供了 2 个参数,通过指定第二个模板类型参数,我们可以使用出 deque 容器外的其它序列式容器,只要该容器支持 empty( )、size( )、back( )、push_back( )、pop_back( ) 这 5 个成员函数即可。
在介绍适配器时提到,序列式容器中同时包含这 5 个成员函数的,有 vector、deque 和 list 这 3 个容器。因此,stack 适配器的基础容器可以是它们 3 个中任何一个。
例如,下面展示了如何定义一个使用 list 基础容器的 stack 适配器:
stack<int, list<int> > values;
- 可以用一个基础容器来初始化 stack 适配器,只要该容器的类型和 stack 底层使用的基础容器类型相同即可。例如:
list<int> values {1, 2, 3};
stack<int,list<int> > my_stack (values);
注意,初始化后的 my_stack 适配器中,栈顶元素为 3,而不是 1。另外在第 2 行代码中,stack 第 2 个模板参数必须显式指定为 list(必须为 int 类型,和存储类型保持一致),否则 stack 底层将默认使用 deque 容器,也就无法用 lsit 容器的内容来初始化 stack 适配器。
- 还可以用一个 stack 适配器来初始化另一个 stack 适配器,只要它们存储的元素类型以及底层采用的基础容器类型相同即可。例如:
list<int > values{ 1, 2, 3 };
stack<int, list<int> > my_stack1(values);
stack<int, list<int> > my_stack=my_stack1;
//stack<int, list<int> > my_stack(my_stack1);
下表列出了 stack 容器支持的全部成员函数:
成员函数 | 功能
empty( ) | 为空返回true,否则返回false
size( ) | 返回存储元素个数(栈的大小)
top( ) | 返回栈顶的引用形式
push(const T&obj) | 将 obj的副本放到栈顶,调用内部的push_back( )
push(T&& obj) | 将obj以移动元素的方式压到栈顶,调用内部的有右值引用参数的push_back( )
emplace(Args&&… args) | 根据既定的排序规则,在栈顶生成元素
pop( ) | 移除栈顶元素
swap(priority_queue& other) | 互换两个元素类型及基础容器类型相同的栈
queue 容器适配器以模板类
queue<T,Container=deque<T> >
其中 T 为存储元素的类型,Container 表示底层容器的类型)的形式位于头文件中,并定义在 std 命名空间里。因此,在创建该容器之前,程序中应包含以下 2 行代码:
#include <queue>
using namespace std;
创建 queue 容器适配器的方式大致可分为以下几种。
- 创建一个空的 queue 容器适配器,其底层使用的基础容器选择默认的 deque 容器:
queue<int> values;
通过此行代码,就可以成功创建一个可存储 int 类型元素,底层采用 deque 容器的 queue 容器适配器。
- 当然,也可以手动指定 queue 容器适配器底层采用的基础容器类型。作为 queue 容器适配器的基础容器,其必须提供 front( )、back( )、push_back( )、pop_front( )、empty( ) 和 size( ) 这几个成员函数,符合条件的序列式容器仅有 deque 和 list。
例如,下面创建了一个使用 list 容器作为基础容器的空 queue 容器适配器:
queue<int, list<int> > values;
注意,在手动指定基础容器的类型时,其存储的数据类型必须和 queue 容器适配器存储的元素类型保持一致。
可以用基础容器来初始化 queue 容器适配器,只要该容器类型和 queue 底层使用的基础容器类型相同即可。例如:
deque<int> values{1,2,3};
queue<int> my_queue(values);
由于 my_queue 底层采用的是 deque 容器,和 values 类型一致,且存储的也都是 int 类型元素,因此可以用 values 对 my_queue 进行初始化。
还可以直接通过 queue 容器适配器来初始化另一个 queue 容器适配器,只要它们存储的元素类型以及底层采用的基础容器类型相同即可。例如:
deque<int> values{1,2,3};
queue<int> my_queue1(values);
queue<int> my_queue(my_queue1);
//或者使用queue<int> my_queue = my_queue1;
注意,和使用基础容器不同,使用 queue 适配器给另一个 queue 进行初始化时,有 2 种方式,使用哪一种都可以。
值得一提的是,第 3、4 种初始化方法中 my_queue 容器适配器的数据是经过拷贝得来的,也就是说,操作 my_queue 容器适配器中的数据,并不会对 values 容器以及my_queue1 容器适配器有任何影响;反过来也是如此。
下表罗列了 queue 容器支持的全部成员函数:
成员函数 | 功能
empty( ) | 为空返回true,否则返回false
size( ) | 返回存储元素个数(队列大小)
front( ) | 返回第一个元素(队首)的引用形式
back( ) | 返回最后一个元素(队尾)的引用形式
push(const T& obj) | 将obj的副本放到队尾,调用内部的push_back( )
emplace(Args&&… args) | 在尾部直接生成元素
pop( ) | 移除队首元素,调用内部的pop_back( )
swap(priority_queue& other) | 互换两个元素类型及基础容器类型相同的队列
和 stack 一样,queue 也没有迭代器,因此访问元素的唯一方式是遍历容器,通过不断移除访问过的元素,去访问下一个元素。
priority_queue 容器适配器模拟的也是队列这种存储结构,即使用此容器适配器存储元素只能“从一端进(称为队尾),从另一端出(称为队头)”,且每次只能访问 priority_queue 中位于队头的元素。
但是,priority_queue 容器适配器中元素的存和取,遵循的并不是 “First in,First out”(先入先出)原则,而是“First in,Largest out”原则。直白的翻译,指的就是先进队列的元素并不一定先出队列,而是优先级最大的元素最先出队列。
由于 priority_queue 容器适配器模板位于头文件中,并定义在 std 命名空间里,因此在试图创建该类型容器之前,程序中需包含以下 2 行代码:
#include <queue>
using namespace std;
创建 priority_queue 容器适配器的方法,大致有以下几种。
1. 创建一个空的 priority_queue 容器适配器,第底层采用默认的 vector 容器,排序方式也采用默认的 less 方法:
priority_queue<int> values;
- 可以使用普通数组或其它容器中指定范围内的数据,对 priority_queue 容器适配器进行初始化:
//使用普通数组
int values[]{4,1,3,2};
priority_queue<int>copy_values(values,values+4);//{4,2,3,1}
//使用序列式容器
array<int,4>values{ 4,1,3,2 };
priority_queue<int>copy_values(values.begin(),values.end());
//{4,2,3,1}
注意,以上 2 种方式必须保证数组或容器中存储的元素类型和 priority_queue 指定的存储类型相同。另外,用来初始化的数组或容器中的数据不需要有序,
priority_queue 会自动对它们进行排序。
- 还可以手动指定 priority_queue 使用的底层容器以及排序规则,比如:
int values[]{ 4,1,2,3 };
priority_queue<int, deque<int>, greater<int> >copy_values(values, values+4);//{1,3,2,4}
事实上,less 和 greater 适用的场景是有限的,更多场景中我们会使用自定义的排序规则。
priority_queue 容器适配器提供了下表所示的这些成员函数:
成员函数 | 功能
empty( ) | 为空返回true,否则返回false
size( ) | 返回存储元素个数(队列大小)
top( ) | 返回第一个元素(队首)的引用形式
push(const T&obj) | 根据既定的排序规则,obj的副本放到对应的位置
push(T&& obj) | 根据既定的排序规则,obj放到对应的位置
emplace(Args&&… args) | 根据既定的排序规则,在适当位置生成元素
pop( ) | 移除队首元素
swap(priority_queue& other) | 互换两个元素类型及基础容器类型相同的优先队列
和 queue 一样,priority_queue 也没有迭代器,因此访问元素的唯一方式是遍历容器,通过不断移除访问过的元素,去访问下一个元素。