Loading...
墨滴

lyf

2021/04/14  阅读:21  主题:雁栖湖

STL容器—deque使用技巧

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::dequestd::vector相比,其内存存储不是连续的,但是也不像std::list是那种完全碎片化的内存,一般std::deque的实现如下图所示,实际数据保存在块(chunk)0,1,2...中,每个chunk是一个vector,大小固定,而维护这些块的"map",也是一个vector,借助"map"间接去对deque中的元素进行获取(index)时需要进行两次转接,比vector的一次直接获取效率略低;
  • std::deque支持同时在首部和尾部进行快速插入和删除(O(1)),而std::vector只支持尾部的快速插入和删除;
  • 相对于std::vectorstd::deque随着元素的动态增长,不涉及已有元素的拷贝复制
  • 如下图所示,std::deque由于要维护其数据结构,其占用的总内存是要多于std::vector的。
stl_deque
stl_deque

2. 时间复杂度

Header
#include <deque>
Constructors
deque<T> d; Make an empty deque. O(1)
deque<T> d(n); Make a deque with N elements. O(n)
deque<T> d(n, value); Make a deque with N elements, initialized to value. O(n)
deque<T> 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.

  1. 标准库中写到,默认还是用vector,如果涉及到同时在首尾需要插入/删除操作的时候,选择deque,这是deque的优势;
  2. deque的内存分配对操作系统而言更"友好",当涉及到大量数据时,例如大于100M,对于vector而言,需要一块连续的100M内存块,但是deque只需要一些小的“内存块拼接起来”;
  3. deque内存增长的时候不需要去对已有数据进行重新拷贝复制。

因此,通过以上的分析,在存储大量数据时,我认为使用std::deque的优先级应该是要高于std::vector的,对于少量数据则尽量用std::vector

有趣的是,标准库中栈(std::stack)的实现默认就是使用了std::deque,虽然栈只涉及一端数据的插入和删除。

  template<typename _Tp, typename _Sequence = deque<_Tp> >
    class stack
    {
       ......
    }

lyf

2021/04/14  阅读:21  主题:雁栖湖

作者介绍

lyf

自动驾驶系统、算法