您的位置:首页 > 产品设计 > UI/UE

deque

2016-03-21 11:57 387 查看
vector是单向开口的连续线性空间,deque是双向开口(在头尾分别做元素删除和插入操作)的连续线性空间

vector与deque的区别:

1)deque允许在头部快速进行元素插入或删除操作,而vector只允许在尾部插入元素,头部只能查看不能插入或删除

2)deque没有容量(capacity)的概念,因为它是动态地以分段连续空间组合而成的,随时可以增加一段新空间并链接起来。不会像vector那样“因旧空间不够而新配置一个更大空间,然后复制元素,再释放旧空间”。

deque的迭代器也是Ramdon Access Iterator,但它的复杂度和vector是不一样的。一般尽可能选vector。

对deque进行排序操行,为了最高效率,可将deque先复制到vector中,将vector排序后(用STL sort算法),再复制到deque。

deque的中控器

deque由一段一段的定量连续空间构成。一旦有必要在deque的前端或尾端增加新空间,便配置一段连续空间,串接在整个deque的头或尾部。deque的最大任务,便是在这些分段的定量连续空间上,维护其整体连续的假象,并提供随机存取的接口。

deque采用一块所谓的map(注意,不是STL的map容器)作为主控。这里所谓map是一小块连续空间,其中每个元素(此处称为一个节点,node)都是指针,指向另一段(较大的)连续线性空间,称为缓冲区。缓冲区才是deque的储存空间主体。SGI STL 允许我们指定缓冲区大小,默认值0表示将使用512 bytes 缓冲区。

template <class T, class Alloc = alloc, size_t BUFSIZ = 0>
class deque {
public:              //基本类型
typedef T value_type;
typedef value_type* pointer;
...
protected:          //内部类型
typedef pointer* map_pointer;
protected:
map_pointer map; //指向map,map是一个T**,也就是一个指针,其所指之物又是指针
size_type map_size; //map内可以容纳多少指针
};


deque是分段连续的,但在外面它又一直维护着整体连续的假象。我们先看看deque的数据结构:

iterator start;               // 起始缓冲区
iterator finish;              // 最后一个缓冲区
// 指向map, map是一个连续的空间, 其每个元素都是一个指向缓冲区的指针
// 其模型见前面的__deque_iterator
map_pointer map;
size_type map_size;   // map容量,有多少个指针


如下是迭代器的结构:

T* cur;       // 此迭代器所指之缓冲区中的现行元素
T* first;     // 此迭代器所指之缓冲区的头
T* last;      // 此迭代器所指之缓冲区的尾(含备用空间)
map_pointer node; //指向管控中心,也就是中控器里的节点,很重要!!


此外迭代器还得完成“整体连续”的假象,如下:

/*
迭代器内对各种指针运算都进行了重载,
所以各种指针运算如加、减、前进、后退等都不能直观视之。
最关键的是:一旦遇到缓冲区边缘,要特别小心,
视前进或后退而定,可能需要调用set_node()跳一个缓冲区
*/
void set_node(map_pointer new_node)
{
node = new_node;
first = *new_node;
last = first + difference_type(buffer_size());
}
};

self& operator++()
{
++cur;       //切换到下一个元素
if (cur == last) {    //如果达到了缓冲区尾端 (“缓冲区”!!注意“last”与“fininsh”)
set_node(node + 1); //就切换至下一节点(也就是缓冲区)
cur = first;        //的第一个元素
}
return *this;
}

// 后缀自增
// 返回当前迭代器的一个副本, 并调用前缀自增运算符实现迭代器自身的自增
self operator++(int)  {
self tmp = *this;
++*this;
return tmp;
}

// 前缀自减, 处理方式类似于前缀自增
// 如果当前迭代器指向元素是当前缓冲区的第一个元素
// 则将迭代器状态调整为前一个缓冲区的最后一个元素
self& operator--()
{
if (cur == first) {
set_node(node - 1);
cur = last;
}
--cur;
return *this;
}

self operator--(int)
{
self tmp = *this;
--*this;
return tmp;
}

//实现随机存取,迭代器可以直接跳跃n个距离
self& operator+=(difference_type n)
{
difference_type offset = n + (cur - first);
if (offset >= 0 && offset < difference_type(buffer_size()))
cur += n; //目标位置在同一缓冲区内
else {  //目标位置不在同一缓冲区内
difference_type node_offset =
offset > 0 ? offset / difference_type(buffer_size())
: -difference_type((-offset - 1) / buffer_size()) - 1;
set_node(node + node_offset);  //切换到正确的节点(缓冲区)
//切换到正确的元素
cur = first + (offset - node_offset * difference_type(buffer_size()));
}
return *this;


deque 的内存管理

deque有自己专属的空间配置器,如下:

protected:
typedef pointer* map_pointer;
//专属空间配置器,每次配置一个元素大小
typedef simple_alloc<value_type, Alloc> data_allocator;
//专属空间配置器,每次配置一个指针大小
typedef simple_alloc<pointer, Alloc> map_allocator;

//构造函数
deque(int n, const value_type& value)
: start(), finish(), map(0), map_size(0)
{
fill_initialize(n, value);
}

template <class T, class Alloc, size_t BufSize>
void deque<T, Alloc, BufSize>::fill_initialize(size_type n,
const value_type& value)
{
create_map_and_nodes(n); //把deque的结构都产生并安排好
map_pointer cur;
__STL_TRY {
//为每个节点的缓冲区设置初值
for (cur = start.node; cur < finish.node; ++cur)
uninitialized_fill(*cur, *cur + buffer_size(), value);
//最后一个缓冲区的设定不太相同
uninitialized_fill(finish.first, finish.cur, value);
}
#       ifdef __STL_USE_EXCEPTIONS
catch(...) {
for (map_pointer n = start.node; n < cur; ++n)
destroy(*n, *n + buffer_size());
destroy_map_and_nodes();
throw;
}
#       endif /* __STL_USE_EXCEPTIONS */
}

// 创建内部使用的map,负责安排产生并安排好deque的结构
template <class T, class Alloc, size_t BufSize>
void deque<T, Alloc, BufSize>::create_map_and_nodes(size_type num_elements)
{
// 需要的结点数 = (元素个数 / 每个缓冲区能容纳的元素数 + 1)
size_type num_nodes = num_elements / buffer_size() + 1;

// map要维护的结点, 这里最小的值为8, 见initial_map_size()
//最大为“所需节点数加2”,前后各预备一个,扩充时可用
map_size = max(initial_map_size(), num_nodes + 2);
//配置有map_size个节点的map
map = map_allocator::allocate(map_size);

// 将[nstart, nfinish)区间设置在map的中间,
// 这样就能保证前后增长而尽可能减少map的重新分配次数
map_pointer nstart = map + (map_size - num_nodes) / 2;
map_pointer nfinish = nstart + num_nodes - 1;

// 分配结点空间
map_pointer cur;
__STL_TRY {
//为map内的每个现用节点配置缓冲区。所有缓冲区加起来就是deque
//的可用空间(最后一个缓冲区可能留有一些富裕)
for (cur = nstart; cur <= nfinish; ++cur)
*cur = allocate_node();
}
#     ifdef  __STL_USE_EXCEPTIONS
catch(...) {
for (map_pointer n = nstart; n < cur; ++n)
deallocate_node(*n);
map_allocator::deallocate(map, map_size);
throw;
}
#     endif /* __STL_USE_EXCEPTIONS */

// 维护指针状态,为deque的两个迭代器start、end设置正确内容
start.set_node(nstart);
finish.set_node(nfinish);
start.cur = start.first;
finish.cur = finish.first + num_elements % buffer_size();
}

//析构函数
~deque()
{
destroy(start, finish);     // <stl_construct.h>
destroy_map_and_nodes();
}

template <class T, class Alloc, size_t BufSize>
void deque<T, Alloc, BufSize>::destroy_map_and_nodes()
{
for (map_pointer cur = start.node; cur <= finish.node; ++cur)
deallocate_node(*cur);
map_allocator::deallocate(map, map_size);
}


deque的元素操作:

push_back(),push_front(),pop_back(),pop_front()

void push_back(const value_type& t)
{
// STL使用前闭后开的区间, 所以如果缓冲区还有一个以上的备用空间,
// 则直接在finish.cur上构造对象即可, 然后更新迭代器
if (finish.cur != finish.last - 1) {
construct(finish.cur, t);
++finish.cur;
}
// 容量已满就要新申请内存了
else
push_back_aux(t);
}

// 仅当finish.cur == finish.last - 1才调用
// 即最后一个缓冲区只剩一个备用空间才调用
// 先配置一块新缓冲区,并设置新元素内容,然后更改迭代器
template <class T, class Alloc, size_t BufSize>
void deque<T, Alloc, BufSize>::push_back_aux(const value_type& t)
{
value_type t_copy = t;
reserve_map_at_back(); //若符合某种条件则必须重换一个map
*(finish.node + 1) = allocate_node(); //配置一个新节点(缓冲区)
__STL_TRY {
construct(finish.cur, t_copy);
finish.set_node(finish.node + 1); //改变finish,令其指向新缓冲区
finish.cur = finish.first; //设定finish的状态
}
__STL_UNWIND(deallocate_node(*(finish.node + 1)));
}

void push_front(const value_type& t)
{
if (start.cur != start.first) { //第一个缓冲区还有备用空间
construct(start.cur - 1, t);  //直接在备用空间上构造元素
--start.cur;  //调整第一缓冲区的使用状态
}
else  //第一缓冲区无备用空间
push_front_aux(t);
}

//  start.cur == start.first.
template <class T, class Alloc, size_t BufSize>
void deque<T, Alloc, BufSize>::push_front_aux(const value_type& t)
{
value_type t_copy = t;
reserve_map_at_front();
*(start.node - 1) = allocate_node(); //若符合某条件则必须重换一个map
__STL_TRY {
start.set_node(start.node - 1); //配置一个新缓冲区
start.cur = start.last - 1;
construct(start.cur, t_copy);
}
#     ifdef __STL_USE_EXCEPTIONS
catch(...) {  //commit or rollback:若非全部成功,就一个不留
start.set_node(start.node + 1);
start.cur = start.first;
deallocate_node(*(start.node - 1));
throw;
}
#     endif /* __STL_USE_EXCEPTIONS */
}

void pop_back()
{
if (finish.cur != finish.first) {
//如果最后一个缓冲区有一个或多个元素
--finish.cur; //调整指针,相当于排除最后元素
destroy(finish.cur); //将最后元素析构
}
else //最后缓冲区没元素
pop_back_aux();  //进行缓冲区的释放
}

//  finish.cur == finish.first.
template <class T, class Alloc, size_t BufSize>
void deque<T, Alloc, BufSize>:: pop_back_aux()
{
deallocate_node(finish.first); //释放最后一个缓冲区
finish.set_node(finish.node - 1);//调整finish的状态,指向
finish.cur = finish.last - 1;    //上一个缓冲区的最后一个元素
destroy(finish.cur);
}

//与pop_back()类似
void pop_front() {
if (start.cur != start.last - 1)
{
destroy(start.cur);
++start.cur;
}
else
pop_front_aux();
}

template <class T, class Alloc, size_t BufSize>
void deque<T, Alloc, BufSize>::pop_front_aux()
{
destroy(start.cur);
deallocate_node(start.first);
start.set_node(start.node + 1);
start.cur = start.first;
}


当前后端空间不足时要进行整治:

reverse_map_at_back(),reverse_map_at_front()

void reserve_map_at_back (size_type nodes_to_add = 1)
{
if (nodes_to_add + 1 > map_size - (finish.node - map))
//如果map尾端的节点备用空间不够
//符合以上条件则必须重换一个map(配置更大,拷贝原来的,释放原空间)
reallocate_map(nodes_to_add, false);
}

void reserve_map_at_front (size_type nodes_to_add = 1)
{
if (nodes_to_add > start.node - map)
//如果map前端的节点备用空间不够
//符合以上条件则必须重换一个map(配置更大,拷贝原来的,释放原空间)
reallocate_map(nodes_to_add, true);
}

// 重新配置map, 不会对缓冲区进行操作, map维护的是指向缓冲区的指针
template <class T, class Alloc, size_t BufSize>
void deque<T, Alloc, BufSize>::reallocate_map(size_type nodes_to_add,
bool add_at_front)
{
size_type old_num_nodes = finish.node - start.node + 1;
size_type new_num_nodes = old_num_nodes + nodes_to_add;

map_pointer new_nstart;
if (map_size > 2 * new_num_nodes) {
new_nstart = map + (map_size - new_num_nodes) / 2
+ (add_at_front ? nodes_to_add : 0);
if (new_nstart < start.node)
copy(start.node, finish.node + 1, new_nstart);
else
copy_backward(start.node, finish.node + 1, new_nstart + old_num_nodes);
}
else {
size_type new_map_size = map_size + max(map_size, nodes_to_add) + 2;

map_pointer new_map = map_allocator::allocate(new_map_size);
new_nstart = new_map + (new_map_size - new_num_nodes) / 2
+ (add_at_front ? nodes_to_add : 0);
copy(start.node, finish.node + 1, new_nstart);
map_allocator::deallocate(map, map_size);

map = new_map;
map_size = new_map_size;
}

start.set_node(new_nstart);
finish.set_node(new_nstart + old_num_nodes - 1);
}


clear(),erase()

//该函数清除整个deque,deque初始状态(无任何元素)时,有一个
//缓冲区,因此clear后恢复初始,要保留一个缓冲区
template <class T, class Alloc, size_t BufSize>
void deque<T, Alloc, BufSize>::clear()
{
// 以下针对头尾以外的每个缓冲区(它们是饱满的)
for (map_pointer node = start.node + 1; node < finish.node; ++node) {
//将缓冲区内所有元素析构
destroy(*node, *node + buffer_size());
//释放缓冲区内存
data_allocator::deallocate(*node, buffer_size());
}

// 至少有头尾两个缓冲区
if (start.node != finish.node) {
destroy(start.cur, start.last); //将头缓冲区目前的元素析构
destroy(finish.first, finish.cur); //将尾缓冲区目前元素析构
//!!!注意:以下释放尾缓冲区,头缓冲区保留
data_allocator::deallocate(finish.first, buffer_size());
}
// 析构所有元素, 但是不释放空间, 因为deque要满足这个前置条件
// 具体的细节见本文件开头'特性'
else
destroy(start.cur, finish.cur);
finish = start;  //调整状态
}

template <class T, class Alloc, size_t BufSize>
deque<T, Alloc, BufSize>::iterator
deque<T, Alloc, BufSize>::erase(iterator first, iterator last)
{
//清楚的是整个deque,直接调用clear()
if (first == start && last == finish) {
clear();
return finish;
}
else {
difference_type n = last - first; //清除区间长度
difference_type elems_before = first - start; //清楚区间前方的元素

//判断清除区间前后哪个元素少
if (elems_before < (size() - n) / 2) { //如果前方元素少,则将前方元素
copy_backward(start, first, last);   //后移(覆盖清除区间)
iterator new_start = start + n;  //标记deque新起点
destroy(start, new_start); //移动完毕,将冗余元素析构
//将冗余缓冲区释放
for (map_pointer cur = start.node; cur < new_start.node; ++cur)
data_allocator::deallocate(*cur, buffer_size());
start = new_start;  //设定deque新起点
}
else { //清除区间后方元素少,向前移动后方元素(覆盖清除区)
copy(last, finish, first);
iterator new_finish = finish - n;
destroy(new_finish, finish);
for (map_pointer cur = new_finish.node + 1; cur <= finish.node; ++cur)
data_allocator::deallocate(*cur, buffer_size());
finish = new_finish;
}
return start + elems_before;
}
}


insert()

template <class T, class Alloc, size_t BufSize>
void deque<T, Alloc, BufSize>::insert(iterator pos,
const_iterator first,
const_iterator last)
{
size_type n = last - first;
if (pos.cur == start.cur) {
iterator new_start = reserve_elements_at_front(n);
__STL_TRY {
uninitialized_copy(first, last, new_start);
start = new_start;
}
__STL_UNWIND(destroy_nodes_at_front(new_start));
}
else if (pos.cur == finish.cur) {
iterator new_finish = reserve_elements_at_back(n);
__STL_TRY {
uninitialized_copy(first, last, finish);
finish = new_finish;
}
__STL_UNWIND(destroy_nodes_at_back(new_finish));
}
else
insert_aux(pos, first, last, n);
}

template <class T, class Alloc, size_t BufSize>
typename deque<T, Alloc, BufSize>::iterator
deque<T, Alloc, BufSize>::insert_aux(iterator pos, const value_type& x)
{
difference_type index = pos - start; //插入点之前的元素个数
value_type x_copy = x;

if (index < size() / 2) {   //如果插入点前元素个数少
push_front(front()); //在最前端加入与第一个元素同值的元素
iterator front1 = start;
++front1;
iterator front2 = front1;
++front2;
pos = start + index;
iterator pos1 = pos;
++pos1;
copy(front2, pos1, front1); //元素移动
}
else { //插入点后的元素个数较少
push_back(back());  //在尾部插入一个与最后一个元素同值的元素
iterator back1 = finish;
--back1;
iterator back2 = back1;
--back2;
pos = start + index;
copy_backward(pos, back2, back1); //元素移动
}
*pos = x_copy; //在插入点上设定新值
return pos;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: