1. std::deque
std::deque (double-ended queue) is an indexed sequence container that allows fast insertion and deletion at both its beginning and its end. In addition, insertion and deletion at either end of a deque never invalidates pointers or references to the rest of the elements.
As opposed to std::vector, the elements of a deque are not stored contiguously: typical implementations use a sequence of individually allocated fixed-size arrays, with additional bookkeeping, which means indexed access to deque must perform two pointer dereferences, compared to vector's indexed access which performs only one.
The storage of a deque is automatically expanded and contracted as needed. Expansion of a deque is cheaper than the expansion of a std::vector because it does not involve copying of the existing elements to a new memory location. On the other hand, deques typically have large minimal memory cost; a deque holding just one element has to allocate its full internal array (e.g. 8 times the object size on 64-bit libstdc++; 16 times the object size or 4096 bytes, whichever is larger, on 64-bit libc++).
std::deque
和std::vector
相比,其内存存储不是连续的,但是也不像std::list
是那种完全碎片化的内存,一般std::deque
的实现如下图所示,实际数据保存在块(chunk)0,1,2...中,每个chunk是一个vector
,大小固定,而维护这些块的"map",也是一个vector
,借助"map"间接去对deque
中的元素进行获取(index)时需要进行两次转接,比vector
的一次直接获取效率略低;std::deque
支持同时在首部和尾部进行快速插入和删除(O(1)),而std::vector
只支持尾部的快速插入和删除;- 相对于
std::vector
,std::deque
随着元素的动态增长,不涉及已有元素的拷贝复制; - 如下图所示,
std::deque
由于要维护其数据结构,其占用的总内存是要多于std::vector
的。
2. 时间复杂度
Header
#include <deque>
Constructors
deque d; | Make an empty deque. | O(1) |
---|---|---|
deque d(n); | Make a deque with N elements. | O(n) |
deque d(n, value); | Make a deque with N elements, initialized to value. | O(n) |
deque d(begin, end); | Make a deque and copy the values from begin to end. | O(n) |
Accessors
d[i]; | Return (or set) the I'th element. | O(1) |
---|---|---|
d.at(i); | Return (or set) the I'th element, with bounds checking. | O(1) |
d.size(); | Return current number of elements. | O(1) |
d.empty(); | Return true if deque is empty. | O(1) |
d.begin(); | Return random access iterator to start. | O(1) |
d.end(); | Return random access iterator to end. | O(1) |
d.front(); | Return the first element. | O(1) |
d.back(); | Return the last element. | O(1) |
std::deque
支持d[i]
访问;- 由于其内存布局,其访问的时间复杂度都为
O(1)
。
Modifiers
d.push_front(value); | Add value to front. | O(1) (amortized) |
---|---|---|
d.push_back(value); | Add value to end. | O(1) (amortized) |
d.emplace_back( ... args ) | Add value to end. | O(1) (amortized) |
d.insert(iterator, value); | Insert value at the position indexed by iterator. | O(n) |
d.pop_front(); | Remove value from front. | O(1) |
d.pop_back(); | Remove value from end. | O(1) |
d.erase(iterator); | Erase value indexed by iterator. | O(n) |
d.erase(begin, end); | Erase the elements from begin to end. | O(n) |
std::deque
在尾部和首部插入和删除的时间复杂度都为O(1)
,当然实际是要略慢于std::vector
的;
3. std::deque的手动内存清理
和std::vector
类似,可以利用swap()
接口去手动释放deque
中的内存,在c++ 11之后,std::deque
新添加了shrink_to_fit()
接口,因此也可以利用此接口去手动释放内存,
std::deque<person> deq;
.....
std::deque<person>().swap(deq); // C++98
deq.shrink_to_fit(); // C++11
4. deque和vector该选哪个?
vector is the type of sequence that should be used by default... deque is the data structure of choice when most insertions and deletions take place at the beginning or at the end of the sequence.
- 标准库中写到,默认还是用
vector
,如果涉及到同时在首尾需要插入/删除操作的时候,选择deque
,这是deque
的优势; deque
的内存分配对操作系统而言更"友好",当涉及到大量数据时,例如大于100M
,对于vector
而言,需要一块连续的100M
内存块,但是deque
只需要一些小的“内存块拼接起来”;deque
内存增长的时候不需要去对已有数据进行重新拷贝复制。
因此,通过以上的分析,在存储大量数据时,我认为使用std::deque
的优先级应该是要高于std::vector
的,对于少量数据则尽量用std::vector
。
有趣的是,标准库中栈(std::stack
)的实现默认就是使用了std::deque
,虽然栈只涉及一端数据的插入和删除。
template<typename _Tp, typename _Sequence = deque<_Tp> >
class stack
{
......
}