std::deque是双端队列,可以高效的在头尾两端插入和删除元素,在std::deque两端插入和删除并不会使其它元素的指针或引用失效。在接口上和std::vector相似。与sdk::vector相反,std::deque中的元素并非连续存储:典型的实现是使用一个单独分配的固定大小数组的序列。std::deque的存储空间会自动按需扩大和缩小。扩大std::deque比扩大std::vector要便宜,因为它不涉及到现有元素复制到新的内存位置。
双端队列(Double-ended queue,缩写为Deque)是一个大小可以动态变化(Dynamic size)且可以在两端扩展或收缩的顺序容器。顺序容器中的元素按照严格的线性顺序排序。可以通过元素在序列中的位置访问对应的元素。不同的库可能会按不同的方式来实现双端队列,通常实现为某种形式的动态数组。但不管通过哪种方式,双端队列都允许通过随机迭代器直接访问各个元素,且内部的存储空间会按需求自动地扩展或收缩。容器实际分配的内存数超过容纳当前所有有效元素所需的,因为额外的内存将被未来增长的部分所使用。就因为这点,当插入元素时,容器不需要太频繁地分配内存。因此,双端队列提供了类似向量(std::vector)的功能,且不仅可以在容器末尾,还可以在容器开头高效地插入或删除元素。但是,相比向量,双端队列不保证内部的元素是按连续的存储空间存储的,因此,不允许对指针直接做偏移操作来直接访问元素。在内部,双端队列与向量的工作方式完全不同:向量使用单数组数据结构,在元素增加的过程中,需要偶尔的内存重分配,而双端队列中的元素被零散地保存在不同的存储块中,容器内部会保存一些必要的数据使得可以以恒定时间及一个统一的顺序接口直接访问任意元素。因此,双端队列的内部实现比向量的稍稍复杂一点,但这也使得它在一些特定环境下可以更高效地增长,特别是对于非常长的序列,内存重分配的代价是及其高昂的。对于大量涉及在除了起始或末尾以外的其它任意位置插入或删除元素的操作,相比列表(std::list)及正向列表(std::forward_list),deque 所表现出的性能是极差的,且操作前后的迭代器、引用的一致性较低。
A deque is very much like a vector: like vector, it is a sequence that supports random access to elements, constant time insertion and removal of elements at the end of these quence, and linear time insertion and removal of elements in the middle. The main way in which deque differs from vector is that deque also supports constant time insertion and removal of elements at the beginning of the sequence. Additionally, deque does not have any member functions analogous to vector's capacity() and reserve(), and does not provide any of the guarantees on iterator validity that are associated with those member functions.
一个容器就是一些特定类型对象的集合。顺序容器(sequential container)为程序员提供了控制元素存储和访问顺序的能力。这种顺序不依赖于元素的值,而是与元素加入容器时的位置相对应。
标准库中的顺序容器包括:
(1)、vector:可变大小数组。支持快速随机访问。在尾部之外的位置插入或删除元素可能很慢。
(2)、deque:双端队列。支持快速随机访问。在头尾位置插入/删除速度很快。
(3)、list:双向链表。只支持双向顺序访问。在list中任何位置进行插入/删除操作速度都很快。
(4)、forward_list:单向链表。只支持单向顺序访问。在链表任何位置进行插入/删除操作速度都很快。
(5)、array:固定大小数组。支持快速随机访问。不能添加或删除元素。
(6)、string:与vector相似的容器,但专门用于保存字符。随机访问快。在尾部插入/删除速度快。
除了固定大小的array外,其它容器都提供高效、灵活的内存管理。我们可以添加和删除元素,扩张和收缩容器的大小。容器保存元素的策略对容器操作的效率有着固定的,有时是重大的影响。在某些情况下,存储策略还会影响特定容器是否支持特定操作。
例如,string和vector将元素保存在连续的内存空间中。由于元素是连续存储的,由元素的下标来计算其地址是非常快速的。但是,在这两种容器的中间位置添加或删除元素就会非常耗时:在一次插入或删除操作后,需要移动插入/删除位置之后的所有元素,来保持连续存储。而且,添加一个元素有时可能还需要分配额外的存储空间。在这种情况下,每个元素都必须移动到新的存储空间中。
list和forward_list两个容器的设计目的是令容器任何位置的添加和删除操作都很快速。作为代价,这两个容器不支持元素的随机访问:为了访问一个元素,我们只能遍历整个容器。而且,与vector、deque和array相比,这两个容器的额外内存开销也很大。
deque是一个更为复杂的数据结构。与string和vector类似,deque支持快速的随机访问。与string和vector一样,在deque的中间位置添加或删除元素的代价(可能)很高。但是,在deque的两端添加或删除元素都是很快的,与list或forward_list添加删除元素的速度相当。
forward_list和array是新C++标准增加的类型。与内置数组相比,array是一个种更安全、更容易使用的数组类型。与内置数组类似,array对象的大小是固定的。因此,array不支持添加和删除元素以及改变容器大小的操作。forward_list的设计目标是达到与最好的手写的单向链表数据结构相当的性能。因此,forward_list没有size操作,因为保存或计算其大小就会比手写链表多出额外的开销。对其他容器而言,size保证是一个快速的常量时间的操作。
通常,使用vector是最好的选择,除法你有很好的理由选择其他容器。
以下是一些选择容器的基本原则:
(1)、除法你有很好的理由选择其他容器,否则应该使用vector;
(2)、如果你的程序有很多小的元素,且空间的额外开销很重要,则不要使用list或forward_list;
(3)、如果程序要求随机访问元素,应使用vector或deque;
(4)、如果程序要求在容器的中间插入或删除元素,应使用list或forward_list;
(5)、如果程序需要在头尾位置插入或删除元素,但不会在中间位置进行插入或删除操作,则使用deque;
(6)、如果程序只有在读取输入时才需要在容器中间位置插入元素,随后需要随机访问元素,则:首先,确定是否真的需要在容器中间位置添加元素。当处理输入数据时,通常可以很容器地向vector追加数据,然后再调用标准库的sort函数来重排容器中的元素,从而避免在中间位置添加元素。如果必须在中间位置插入元素,考虑在输入阶段使用list,一旦输入完成,将list中的内容拷贝到一个vector中。
如果你不确定应该使用哪种容器,那么可以在程序中只使用vector和list公共的操作:使用迭代器,不使用下标操作,避免随机访问。这样,在必要时选择使用vector或list都很方便。
一般来说,每个容器都定义在一个头文件中,文件名与类型名相同。即,deque定义在头文件deque中,list定义在头文件list中,以此类推。容器均定义为模板类。
顺序容器几乎可以保存任意类型的元素。特别是,我们可以定义一个容器,其元素的类型是另一个容器。这种容器的定义与任何其他容器类型完全一样:在尖括号中指定元素类型(此种情况下,是另一种容器类型)。
除了顺序容器外,标准库还定义了三个顺序容器适配器:stack、queue和priority_queue。适配器(adaptor)是标准库中的一个通用概念。容器、迭代器和函数都有适配器。本质上,一个适配器是一种机制,能使某种事物的行为看起来像另外一种事物一样。一个容器适配器接受一种已有的容器类型,使其行为看起来像一种不同的类型。
下面是从其他文章中copy的测试代码,详细内容介绍可以参考对应的reference:
#include "deque.hpp"
#include
#include
#include
// https://msdn.microsoft.com/en-us/library/22a9t119.aspx
int test_deque_2()
{
{ // deque::at: Returns a reference to the element at a specified location in the deque.
using namespace std;
deque c1;
c1.push_back(10);
c1.push_back(20);
const int& i = c1.at(0);
int& j = c1.at(1);
cout << "The first element is " << i << endl;
cout << "The second element is " << j << endl;
}
{ // deque::back: Returns a reference to the last element of the deque.
using namespace std;
deque c1;
c1.push_back(10);
c1.push_back(11);
int& i = c1.back();
const int& ii = c1.front();
cout << "The last integer of c1 is " << i << endl; // 11
i--;
cout << "The next-to-last integer of c1 is " << ii << endl; // 10
cout << "The last integer of c1 is " << c1.back() << endl; // 10
}
{ // deque::clear: Erases all the elements of a deque.
using namespace std;
deque c1;
c1.push_back(10);
c1.push_back(20);
c1.push_back(30);
cout << "The size of the deque is initially " << c1.size() << endl;
c1.clear();
cout << "The size of the deque after clearing is " << c1.size() << endl;
}
{ // deque::const_reference: A type that provides a reference to a const element stored in a deque for reading and performing const operations
using namespace std;
deque c1;
c1.push_back(10);
c1.push_back(20);
const deque c2 = c1;
const int &i = c2.front();
const int &j = c2.back();
cout << "The first element is " << i << endl;
cout << "The second element is " << j << endl;
// The following line would cause an error as c2 is const
// c2.push_back( 30 );
}
{ // deque::crbegin: Returns a const iterator to the first element in a reversed deque
using namespace std;
deque v1;
deque ::iterator v1_Iter;
deque ::const_reverse_iterator v1_rIter;
v1.push_back(1);
v1.push_back(2);
v1_Iter = v1.begin();
cout << "The first element of deque is "
<< *v1_Iter << "." << endl;
v1_rIter = v1.crbegin();
cout << "The first element of the reversed deque is "
<< *v1_rIter << "." << endl;
}
{ // deque::emplace: Inserts an element constructed in place into the deque at a specified position.
using namespace std;
deque v1;
deque ::iterator Iter;
v1.push_back(10);
v1.push_back(20);
v1.push_back(30);
cout << "v1 =";
for (Iter = v1.begin(); Iter != v1.end(); Iter++)
cout << " " << *Iter;
cout << endl;
// initialize a deque of deques by moving v1
deque < deque > vv1;
vv1.emplace(vv1.begin(), move(v1));
if (vv1.size() != 0 && vv1[0].size() != 0) {
cout << "vv1[0] =";
for (Iter = vv1[0].begin(); Iter != vv1[0].end(); Iter++)
cout << " " << *Iter;
cout << endl;
}
}
return 0;
}
/
// reference: http://www.cplusplus.com/reference/deque/deque/
int test_deque_1()
{
{ // deque::deque: Construct deque container
unsigned int i;
// constructors used in the same order as described above:
std::deque first; // empty deque of ints
std::deque second(4, 100); // four ints with value 100
std::deque third(second.begin(), second.end()); // iterating through second
std::deque fourth(third); // a copy of third
// the iterator constructor can be used to copy arrays:
int myints[] = { 16, 2, 77, 29 };
std::deque fifth(myints, myints + sizeof(myints) / sizeof(int));
std::cout << "The contents of fifth are:";
for (std::deque::iterator it = fifth.begin(); it != fifth.end(); ++it)
std::cout << ' ' << *it; // 16 2 77 29
std::cout << '\n';
}
{ // deque::assign: Assigns new contents to the deque container,
// replacing its current contents, and modifying its size accordingly.
std::deque first;
std::deque second;
std::deque third;
first.assign(7, 100); // 7 ints with a value of 100
std::deque::iterator it;
it = first.begin() + 1;
second.assign(it, first.end() - 1); // the 5 central values of first
int myints[] = { 1776, 7, 4 };
third.assign(myints, myints + 3); // assigning from array.
std::cout << "Size of first: " << int(first.size()) << '\n'; // 7
std::cout << "Size of second: " << int(second.size()) << '\n'; // 5
std::cout << "Size of third: " << int(third.size()) << '\n'; // 3
}
{ // deque::at: Returns a reference to the element at position n in the deque container object.
std::deque mydeque(10); // 10 zero-initialized unsigneds
// assign some values:
for (unsigned i = 0; i < mydeque.size(); i++)
mydeque.at(i) = i;
std::cout << "mydeque contains:";
for (unsigned i = 0; i < mydeque.size(); i++)
std::cout << ' ' << mydeque.at(i); // 0 1 2 3 4 5 6 7 8 9
std::cout << '\n';
}
{ // deque::back: Returns a reference to the last element in the container.
// deque::push_back: Adds a new element at the end of the deque container,
// after its current last element. The content of val is copied (or moved) to the new element
std::deque mydeque;
mydeque.push_back(10);
while (mydeque.back() != 0)
mydeque.push_back(mydeque.back() - 1);
std::cout << "mydeque contains:";
for (std::deque::iterator it = mydeque.begin(); it != mydeque.end(); ++it)
std::cout << ' ' << *it;
std::cout << '\n';
}
{ // deque::begin: Return iterator to beginning
// deque::end: Return iterator to end
std::deque mydeque;
for (int i = 1; i <= 5; i++) mydeque.push_back(i);
std::cout << "mydeque contains:";
std::deque::iterator it = mydeque.begin();
while (it != mydeque.end())
std::cout << ' ' << *it++;
std::cout << '\n';
}
{ // deque::cbegin: c++11, Return const_iterator to beginning
// deque::cend: c++11, Return const_iterator to end
std::deque mydeque = { 10, 20, 30, 40, 50 };
std::cout << "mydeque contains:";
for (auto it = mydeque.cbegin(); it != mydeque.cend(); ++it)
std::cout << ' ' << *it;
std::cout << '\n';
}
{ // deque::clear: Clear content
unsigned int i;
std::deque mydeque;
mydeque.push_back(100);
mydeque.push_back(200);
mydeque.push_back(300);
std::cout << "mydeque contains:";
for (std::deque::iterator it = mydeque.begin(); it != mydeque.end(); ++it)
std::cout << ' ' << *it;
std::cout << '\n';
mydeque.clear();
mydeque.push_back(1101);
mydeque.push_back(2202);
std::cout << "mydeque contains:";
for (std::deque::iterator it = mydeque.begin(); it != mydeque.end(); ++it)
std::cout << ' ' << *it;
std::cout << '\n';
}
{ // deque::crbegin: c++11, Return const_reverse_iterator to reverse beginning
// deque::crend: c++11, Return const_reverse_iterator to reverse end
std::deque mydeque = { 1, 2, 3, 4, 5 };
std::cout << "mydeque backwards:";
for (auto rit = mydeque.crbegin(); rit != mydeque.crend(); ++rit)
std::cout << ' ' << *rit;
std::cout << '\n';
}
{ // deque::emplace: c++11, Construct and insert element
std::deque mydeque = { 10, 20, 30 };
auto it = mydeque.emplace(mydeque.begin() + 1, 100);
mydeque.emplace(it, 200);
mydeque.emplace(mydeque.end(), 300);
std::cout << "mydeque contains:";
for (auto& x : mydeque)
std::cout << ' ' << x;
std::cout << '\n';
}
{ // deque::emplace_back: c++11, Construct and insert element at the end
std::deque mydeque = { 10, 20, 30 };
mydeque.emplace_back(100);
mydeque.emplace_back(200);
std::cout << "mydeque contains:";
for (auto& x : mydeque)
std::cout << ' ' << x;
std::cout << '\n';
}
{ // deque::emplace_front: c++11, Construct and insert element at beginning
std::deque mydeque = { 10, 20, 30 };
mydeque.emplace_front(111);
mydeque.emplace_front(222);
std::cout << "mydeque contains:";
for (auto& x : mydeque)
std::cout << ' ' << x;
std::cout << '\n';
}
{ // deque::empty: Test whether container is empty
std::deque mydeque;
int sum(0);
for (int i = 1; i <= 10; i++) mydeque.push_back(i);
while (!mydeque.empty()) {
sum += mydeque.front();
mydeque.pop_front();
}
std::cout << "total: " << sum << '\n';
}
{ // deque::erase: Erase elements
std::deque mydeque;
// set some values (from 1 to 10)
for (int i = 1; i <= 10; i++) mydeque.push_back(i);
// erase the 6th element
mydeque.erase(mydeque.begin() + 5);
// erase the first 3 elements:
mydeque.erase(mydeque.begin(), mydeque.begin() + 3);
std::cout << "mydeque contains:";
for (std::deque::iterator it = mydeque.begin(); it != mydeque.end(); ++it)
std::cout << ' ' << *it;
std::cout << '\n';
}
{ // deque::front: Access first element, Returns a reference to the first element in the deque containe
// deque::push_front: Inserts a new element at the beginning of the deque container,
// right before its current first element. The content of val is copied (or moved) to the inserted element
std::deque mydeque;
mydeque.push_front(77);
mydeque.push_back(20);
mydeque.front() -= mydeque.back();
std::cout << "mydeque.front() is now " << mydeque.front() << '\n';
}
{ // deque::get_allocator: Returns a copy of the allocator object associated with the deque object
std::deque mydeque;
int * p;
unsigned int i;
// allocate an array with space for 5 elements using deque's allocator:
p = mydeque.get_allocator().allocate(5);
// construct values in-place on the array:
for (i = 0; i < 5; i++) mydeque.get_allocator().construct(&p[i], i);
std::cout << "The allocated array contains:";
for (i = 0; i < 5; i++) std::cout << ' ' << p[i];
std::cout << '\n';
// destroy and deallocate:
for (i = 0; i < 5; i++) mydeque.get_allocator().destroy(&p[i]);
mydeque.get_allocator().deallocate(p, 5);
}
{ // deque::insert: Insert elements
std::deque mydeque;
// set some initial values:
for (int i = 1; i < 6; i++) mydeque.push_back(i); // 1 2 3 4 5
std::deque::iterator it = mydeque.begin();
++it;
it = mydeque.insert(it, 10); // 1 10 2 3 4 5
// "it" now points to the newly inserted 10
mydeque.insert(it, 2, 20); // 1 20 20 10 2 3 4 5
// "it" no longer valid!
it = mydeque.begin() + 2;
std::vector myvector(2, 30);
mydeque.insert(it, myvector.begin(), myvector.end());
// 1 20 30 30 20 10 2 3 4 5
std::cout << "mydeque contains:";
for (it = mydeque.begin(); it != mydeque.end(); ++it)
std::cout << ' ' << *it;
std::cout << '\n';
}
{ // deque::max_size: Return maximum size
unsigned int i;
std::deque mydeque;
std::cout << "Enter number of elements: ";
i = 100; //std::cin >> i;
if (i < mydeque.max_size()) mydeque.resize(i);
else std::cout << "That size exceeds the limit.\n";
fprintf(stderr, "max size: %d\n", mydeque.max_size());
}
{ // deque::operator=: Assigns new contents to the container, replacing its current contents, and modifying its size accordingly
std::deque first(3); // deque with 3 zero-initialized ints
std::deque second(5); // deque with 5 zero-initialized ints
second = first;
first = std::deque();
std::cout << "Size of first: " << int(first.size()) << '\n';
std::cout << "Size of second: " << int(second.size()) << '\n';
}
{ // deque::operator[]: Returns a reference to the element at position n in the deque container
std::deque mydeque(10); // 10 zero-initialized elements
std::deque::size_type sz = mydeque.size();
// assign some values:
for (unsigned i = 0; i < sz; i++) mydeque[i] = i;
// reverse order of elements using operator[]:
for (unsigned i = 0; i < sz / 2; i++) {
int temp;
temp = mydeque[sz - 1 - i];
mydeque[sz - 1 - i] = mydeque[i];
mydeque[i] = temp;
}
// print content:
std::cout << "mydeque contains:";
for (unsigned i = 0; i < sz; i++)
std::cout << ' ' << mydeque[i];
std::cout << '\n';
}
{ // deque::pop_back: Removes the last element in the deque container, effectively reducing the container size by one
std::deque mydeque;
int sum(0);
mydeque.push_back(10);
mydeque.push_back(20);
mydeque.push_back(30);
while (!mydeque.empty()) {
sum += mydeque.back();
mydeque.pop_back();
}
std::cout << "The elements of mydeque add up to " << sum << '\n';
}
{ // deque::pop_front: Removes the first element in the deque container, effectively reducing its size by one.
std::deque mydeque;
mydeque.push_back(100);
mydeque.push_back(200);
mydeque.push_back(300);
std::cout << "Popping out the elements in mydeque:";
while (!mydeque.empty()) {
std::cout << ' ' << mydeque.front();
mydeque.pop_front();
}
std::cout << "\nThe final size of mydeque is " << int(mydeque.size()) << '\n';
}
{ // deque::rbegin: Returns a reverse iterator pointing to the last element in the container
// deque::rend: Returns a reverse iterator pointing to the theoretical element preceding the first element in the deque container
std::deque mydeque(5); // 5 default-constructed ints
std::deque::reverse_iterator rit = mydeque.rbegin();
int i = 0;
for (rit = mydeque.rbegin(); rit != mydeque.rend(); ++rit)
*rit = ++i;
std::cout << "mydeque contains:";
for (std::deque::iterator it = mydeque.begin(); it != mydeque.end(); ++it)
std::cout << ' ' << *it;
std::cout << '\n';
}
{ // deque::resize: Resizes the container so that it contains n elements
std::deque mydeque;
std::deque::iterator it;
// set some initial content:
for (int i = 1; i < 10; ++i) mydeque.push_back(i);
mydeque.resize(5);
mydeque.resize(8, 100);
mydeque.resize(12);
std::cout << "mydeque contains:";
for (std::deque::iterator it = mydeque.begin(); it != mydeque.end(); ++it)
std::cout << ' ' << *it;
std::cout << '\n';
}
{ // deque::shrink_to_fit: c++11, Requests the container to reduce its memory usage to fit its size.
// deque::size: Returns the number of elements in the deque container
std::deque mydeque(100);
std::cout << "1. size of mydeque: " << mydeque.size() << '\n';
mydeque.resize(10);
std::cout << "2. size of mydeque: " << mydeque.size() << '\n';
mydeque.shrink_to_fit();
fprintf(stderr, "3. size of mydeque: %d\n", mydeque.size());
}
{ // deque::swap: Exchanges the content of the container by the content of x,
// which is another deque object containing elements of the same type. Sizes may differ.
unsigned int i;
std::deque foo(3, 100); // three ints with a value of 100
std::deque bar(5, 200); // five ints with a value of 200
foo.swap(bar);
std::cout << "foo contains:";
for (std::deque::iterator it = foo.begin(); it != foo.end(); ++it)
std::cout << ' ' << *it;
std::cout << '\n';
std::cout << "bar contains:";
for (std::deque::iterator it = bar.begin(); it != bar.end(); ++it)
std::cout << ' ' << *it;
std::cout << '\n';
}
{ // relational operators: compare
std::deque foo(3, 100); // three ints with a value of 100
std::deque bar(2, 200); // two ints with a value of 200
if (foo == bar) std::cout << "foo and bar are equal\n";
if (foo != bar) std::cout << "foo and bar are not equal\n";
if (foo< bar) std::cout << "foo is less than bar\n";
if (foo> bar) std::cout << "foo is greater than bar\n";
if (foo <= bar) std::cout << "foo is less than or equal to bar\n";
if (foo >= bar) std::cout << "foo is greater than or equal to bar\n";
}
{ // std::swap: The contents of container x are exchanged with those of y.
// Both container objects must be of the same type (same template parameters), although sizes may differ
unsigned int i;
std::deque foo(3, 100); // three ints with a value of 100
std::deque bar(5, 200); // five ints with a value of 200
swap(foo, bar);
std::cout << "foo contains:";
for (std::deque::iterator it = foo.begin(); it != foo.end(); ++it)
std::cout << ' ' << *it;
std::cout << '\n';
std::cout << "bar contains:";
for (std::deque::iterator it = bar.begin(); it != bar.end(); ++it)
std::cout << ' ' << *it;
std::cout << '\n';
}
return 0;
}