1 deque概述 双端队列
vector是单向开口的连续线性空间, list是不连续空间, deque是双向开口的连续空间:双向开口就是头尾都可以插入删除元素
vector虽然是可以扩容,但是其扩容是先分配再复制后释放,但是deque允许在常数时间内在头和尾插入删除元素,deque没有容量的概念,无capacity有size, 是由分段连续空间构成的,那么deque的重点就是,如何在操作中保持分段连续空间的连接
vector的迭代器是支持随机访问的普通指针,虽然deque也支持随机访问,但不是普通指针
尽可能选择vector,对deque排序可以先复制到vector中排序
2 deque中控器
逻辑上连续空间,实际上分段连续空间,通过中控器map达到连续空间的假象,map也是一小段连续空间,每个元素node都是一个指针,指向一个较大的连续空间,称为缓冲区,缓冲区才是存储数据的主体,默认是512bytes.
template<class T, class Alloc= alloc, size_t buffer_size=0>
class deque
{
typedef T value_type;
typedef value_type* pointer;
typedef pointer* map_pointer;
map_pointer map; //T** 指针的指针
size_type map_size;
}
3 deque迭代器
deque的迭代器应该能判断自己是否处于缓冲区的边缘,能够跳转到上一个或者下一个缓冲区
迭代器最重要的操作就是operator++ 和operator-- 以及set_node()
_Elt_pointer _M_cur; //cur 指向当前缓冲区中的先行元素
_Elt_pointer _M_first;//指向当前缓冲区的开头
_Elt_pointer _M_last;//指向当前缓冲区的结尾
_Map_pointer _M_node;//指向中控节点的当前缓冲区
//下面直接复制源码 + 注释
reference
operator*() const _GLIBCXX_NOEXCEPT //解引用, *cur
{ return *_M_cur; }
pointer
operator->() const _GLIBCXX_NOEXCEPT // ->
{ return _M_cur; }
_Self&
operator++() _GLIBCXX_NOEXCEPT // 后移一个元素
{
++_M_cur;
if (_M_cur == _M_last) //判断是否到达当前缓冲区的结尾
{
_M_set_node(_M_node + 1); //使用set_node跳转
_M_cur = _M_first;
}
return *this;
}
_Self
operator++(int) _GLIBCXX_NOEXCEPT //经典写法
{
_Self __tmp = *this;
++*this;
return __tmp;
}
_Self&
operator--() _GLIBCXX_NOEXCEPT
{
if (_M_cur == _M_first) //同理判断是否在当前缓冲区de
{
_M_set_node(_M_node - 1);
_M_cur = _M_last;
}
--_M_cur;
return *this;
}
_Self
operator--(int) _GLIBCXX_NOEXCEPT
{
_Self __tmp = *this;
--*this;
return __tmp;
}
_Self&
operator+=(difference_type __n) _GLIBCXX_NOEXCEPT //随机访问
{
const difference_type __offset = __n + (_M_cur - _M_first);
if (__offset >= 0 && __offset < difference_type(_S_buffer_size())) //在同一个缓冲区内
_M_cur += __n;
else//不在同一个缓冲区内
{
const difference_type __node_offset =
__offset > 0 ? __offset / difference_type(_S_buffer_size())
: -difference_type((-__offset - 1)
/ _S_buffer_size()) - 1;
_M_set_node(_M_node + __node_offset);
_M_cur = _M_first + (__offset - __node_offset
* difference_type(_S_buffer_size()));
}
return *this;
}
_Self
operator+(difference_type __n) const _GLIBCXX_NOEXCEPT
{
_Self __tmp = *this;
return __tmp += __n;
}
_Self&
operator-=(difference_type __n) _GLIBCXX_NOEXCEPT
{ return *this += -__n; }
_Self
operator-(difference_type __n) const _GLIBCXX_NOEXCEPT
{
_Self __tmp = *this;
return __tmp -= __n;
}
reference
operator[](difference_type __n) const _GLIBCXX_NOEXCEPT
{ return *(*this + __n); }
/**
* Prepares to traverse new_node. Sets everything except
* _M_cur, which should therefore be set by the caller
* immediately afterwards, based on _M_first and _M_last.
*/
void
_M_set_node(_Map_pointer __new_node) _GLIBCXX_NOEXCEPT //跳一个缓冲区
{
_M_node = __new_node;
_M_first = *__new_node;
_M_last = _M_first + difference_type(_S_buffer_size());
}
4 deque数据结构
struct _Deque_impl
: public _Tp_alloc_type
{
_Map_pointer _M_map; //T**的map指针
size_t _M_map_size; //map大小
iterator _M_start;//指向第一个缓冲区的第一个元素
iterator _M_finish;//指向最后一个缓冲区的最后一个元素
}
reference
operator[](size_type __n) _GLIBCXX_NOEXCEPT
{
__glibcxx_requires_subscript(__n);
return this->_M_impl._M_start[difference_type(__n)];
}
reference
front() _GLIBCXX_NOEXCEPT
{
__glibcxx_requires_nonempty();
return *begin();//*strat
}
reference
back() _GLIBCXX_NOEXCEPT
{
__glibcxx_requires_nonempty();
iterator __tmp = end();
--__tmp;
return *__tmp;//end()上一个元素
}
iterator
begin() _GLIBCXX_NOEXCEPT
{ return this->_M_impl._M_start; } //start
iterator
end() _GLIBCXX_NOEXCEPT
{ return this->_M_impl._M_finish; }//finish
size_type
size() const _GLIBCXX_NOEXCEPT
{ return this->_M_impl._M_finish - this->_M_impl._M_start; } //finish - start
/** Returns the size() of the largest possible %deque. */
size_type
max_size() const _GLIBCXX_NOEXCEPT
{ return _Alloc_traits::max_size(_M_get_Tp_allocator()); }
empty() const _GLIBCXX_NOEXCEPT
{ return this->_M_impl._M_finish == this->_M_impl._M_start; }//finish == start
5 deque内存构造
deque(size_type __n, const value_type& __value,
const allocator_type& __a = allocator_type())
: _Base(__a, __n)
{ _M_fill_initialize(__value); }
template <typename _Tp, typename _Alloc>
void
deque<_Tp, _Alloc>::
_M_fill_initialize(const value_type& __value)
{
_Map_pointer __cur;
__try
{
for (__cur = this->_M_impl._M_start._M_node;
__cur < this->_M_impl._M_finish._M_node;
++__cur)//对每一个缓冲区设置为__value
std::__uninitialized_fill_a(*__cur, *__cur + _S_buffer_size(),
__value, _M_get_Tp_allocator());
std::__uninitialized_fill_a(this->_M_impl._M_finish._M_first,
this->_M_impl._M_finish._M_cur,
__value, _M_get_Tp_allocator());//最后一个缓冲区除去备用空间设置缓冲区
}
__catch(...)
{
std::_Destroy(this->_M_impl._M_start, iterator(*__cur, __cur),
_M_get_Tp_allocator());
__throw_exception_again;
}
}
//初始化map
template<typename _Tp, typename _Alloc>
void
_Deque_base<_Tp, _Alloc>::
_M_initialize_map(size_t __num_elements)
{
const size_t __num_nodes = (__num_elements/ __deque_buf_size(sizeof(_Tp))
+ 1);//需要几个缓冲区,最少8个
this->_M_impl._M_map_size = std::max((size_t) _S_initial_map_size,
size_t(__num_nodes + 2));
this->_M_impl._M_map = _M_allocate_map(this->_M_impl._M_map_size);
// For "small" maps (needing less than _M_map_size nodes), allocation
// starts in the middle elements and grows outwards. So nstart may be
// the beginning of _M_map, but for small maps it may be as far in as
// _M_map+3.
_Map_pointer __nstart = (this->_M_impl._M_map
+ (this->_M_impl._M_map_size - __num_nodes) / 2);//保持在map的中间,这样头尾插入删除都是差不多的
_Map_pointer __nfinish = __nstart + __num_nodes;
__try
{ _M_create_nodes(__nstart, __nfinish); }
__catch(...)
{
_M_deallocate_map(this->_M_impl._M_map, this->_M_impl._M_map_size);
this->_M_impl._M_map = _Map_pointer();
this->_M_impl._M_map_size = 0;
__throw_exception_again;
}
//设置迭代器start finish
this->_M_impl._M_start._M_set_node(__nstart);
this->_M_impl._M_finish._M_set_node(__nfinish - 1);
this->_M_impl._M_start._M_cur = _M_impl._M_start._M_first;
this->_M_impl._M_finish._M_cur = (this->_M_impl._M_finish._M_first
+ __num_elements
% __deque_buf_size(sizeof(_Tp)));
}
6 deque其他操作
6.1 push_back()
void
push_back(const value_type& __x)
{
if (this->_M_impl._M_finish._M_cur
!= this->_M_impl._M_finish._M_last - 1) //还有没有备用空间: 有
{
_Alloc_traits::construct(this->_M_impl,
this->_M_impl._M_finish._M_cur, __x);
++this->_M_impl._M_finish._M_cur;//直接在当前缓冲区构造一个
}
else
_M_push_back_aux(__x);//没有
}
//_M_push_back_aux(__x)
template<typename _Tp, typename _Alloc>
#if __cplusplus >= 201103L
template<typename... _Args>
void
deque<_Tp, _Alloc>::
_M_push_back_aux(_Args&&... __args)
#else
void
deque<_Tp, _Alloc>::
_M_push_back_aux(const value_type& __t)
#endif
{
_M_reserve_map_at_back();//map不够用就换一个map,见下面如何换map
*(this->_M_impl._M_finish._M_node + 1) = this->_M_allocate_node();//重新分配一个node缓冲区
__try
{
#if __cplusplus >= 201103L
_Alloc_traits::construct(this->_M_impl,
this->_M_impl._M_finish._M_cur,
std::forward<_Args>(__args)...);//设置元素的值
#else
//重新设置finish迭代器的值
this->_M_impl.construct(this->_M_impl._M_finish._M_cur, __t);
#endif
this->_M_impl._M_finish._M_set_node(this->_M_impl._M_finish._M_node
+ 1);//跳一个node
this->_M_impl._M_finish._M_cur = this->_M_impl._M_finish._M_first;
}
__catch(...)
{
_M_deallocate_node(*(this->_M_impl._M_finish._M_node + 1));
__throw_exception_again;
}
}
//换map
void
_M_reserve_map_at_front(size_type __nodes_to_add = 1)
{
if (__nodes_to_add > size_type(this->_M_impl._M_start._M_node
- this->_M_impl._M_map))
_M_reallocate_map(__nodes_to_add, true); //类似于vector(连续空间):配置新空间 拷贝旧空间 释放旧空间
}
template <typename _Tp, typename _Alloc>
void
deque<_Tp, _Alloc>::
_M_reallocate_map(size_type __nodes_to_add, bool __add_at_front)
{
const size_type __old_num_nodes
= this->_M_impl._M_finish._M_node - this->_M_impl._M_start._M_node + 1; //计算旧的节点个数
const size_type __new_num_nodes = __old_num_nodes + __nodes_to_add;//新节点个数
_Map_pointer __new_nstart;
if (this->_M_impl._M_map_size > 2 * __new_num_nodes)
{
__new_nstart = this->_M_impl._M_map + (this->_M_impl._M_map_size
- __new_num_nodes) / 2
+ (__add_at_front ? __nodes_to_add : 0);
if (__new_nstart < this->_M_impl._M_start._M_node)
std::copy(this->_M_impl._M_start._M_node,
this->_M_impl._M_finish._M_node + 1,
__new_nstart);
else
std::copy_backward(this->_M_impl._M_start._M_node,
this->_M_impl._M_finish._M_node + 1,
__new_nstart + __old_num_nodes);
}
else
{
size_type __new_map_size = this->_M_impl._M_map_size
+ std::max(this->_M_impl._M_map_size,
__nodes_to_add) + 2;
//配置一块新的空间
_Map_pointer __new_map = this->_M_allocate_map(__new_map_size);
__new_nstart = __new_map + (__new_map_size - __new_num_nodes) / 2
+ (__add_at_front ? __nodes_to_add : 0);
//拷贝
std::copy(this->_M_impl._M_start._M_node,
this->_M_impl._M_finish._M_node + 1,
__new_nstart);
//释放
_M_deallocate_map(this->_M_impl._M_map, this->_M_impl._M_map_size);
this->_M_impl._M_map = __new_map;
this->_M_impl._M_map_size = __new_map_size;
}
this->_M_impl._M_start._M_set_node(__new_nstart);
this->_M_impl._M_finish._M_set_node(__new_nstart + __old_num_nodes - 1);
}
6.2 push_front()
和push_back一样的,就是处理的是头部,就不再详细写了
void
push_front(const value_type& __x)
{
if (this->_M_impl._M_start._M_cur != this->_M_impl._M_start._M_first)
{
_Alloc_traits::construct(this->_M_impl,
this->_M_impl._M_start._M_cur - 1,
__x);
--this->_M_impl._M_start._M_cur;
}
else
_M_push_front_aux(__x);
}
6.3 pop_back()
和push_back()差不多 看是否是在finish的头部,要删除缓冲区
void
pop_back() _GLIBCXX_NOEXCEPT
{
__glibcxx_requires_nonempty();
if (this->_M_impl._M_finish._M_cur
!= this->_M_impl._M_finish._M_first)//不在finish的头部
{
--this->_M_impl._M_finish._M_cur;
_Alloc_traits::destroy(this->_M_impl,
this->_M_impl._M_finish._M_cur);//删除当前元素即可
}
else
_M_pop_back_aux(); //在:需要删除缓冲区
}
template <typename _Tp, typename _Alloc>
void deque<_Tp, _Alloc>::
_M_pop_back_aux()
{
_M_deallocate_node(this->_M_impl._M_finish._M_first);//删除finish节点缓冲区
this->_M_impl._M_finish._M_set_node(this->_M_impl._M_finish._M_node - 1);//重新配置finish迭代器 向前跳一个
this->_M_impl._M_finish._M_cur = this->_M_impl._M_finish._M_last - 1;
_Alloc_traits::destroy(_M_get_Tp_allocator(),
this->_M_impl._M_finish._M_cur);
}
6.4 pop_front()
和pop_back()差不多,就是看在不在start节点的最后一个 要是在就需要删除缓冲区
void
pop_front() _GLIBCXX_NOEXCEPT
{
__glibcxx_requires_nonempty();
if (this->_M_impl._M_start._M_cur
!= this->_M_impl._M_start._M_last - 1)
{
_Alloc_traits::destroy(this->_M_impl,
this->_M_impl._M_start._M_cur);
++this->_M_impl._M_start._M_cur;
}
else
_M_pop_front_aux();
}
6.5 erase()
//清除一个元素po
template <typename _Tp, typename _Alloc>
typename deque<_Tp, _Alloc>::iterator
deque<_Tp, _Alloc>::
_M_erase(iterator __position)
{
iterator __next = __position;
++__next;
const difference_type __index = __position - begin();//清除点之前的元素个数
if (static_cast<size_type>(__index) < (size() >> 1)) //清除点之前元素少:从前面删除
{
if (__position != begin())
_GLIBCXX_MOVE_BACKWARD3(begin(), __position, __next); //move_backward 移动清除点之前的元素
pop_front();//删除最前面的元素
}
else
{
if (__next != end())
_GLIBCXX_MOVE3(__next, end(), __position);//从后面删除
pop_back();
}
return begin() + __index;
}
//清除一个区间 :和一个元素差不多 就是看看前面少还是后面少,在选择从前面删除还是后面
template <typename _Tp, typename _Alloc>
typename deque<_Tp, _Alloc>::iterator
deque<_Tp, _Alloc>::
_M_erase(iterator __first, iterator __last)
{
if (__first == __last)
return __first;
else if (__first == begin() && __last == end())
{
clear();
return end();
}
else
{
const difference_type __n = __last - __first;
const difference_type __elems_before = __first - begin();
if (static_cast<size_type>(__elems_before) <= (size() - __n) / 2)
{
if (__first != begin())
_GLIBCXX_MOVE_BACKWARD3(begin(), __first, __last);
_M_erase_at_begin(begin() + __n);
}
else
{
if (__last != end())
_GLIBCXX_MOVE3(__last, end(), __first);
_M_erase_at_end(end() - __n);
}
return begin() + __elems_before;
}
}
6.6 insert()
typename deque<_Tp, _Alloc>::iterator
deque<_Tp, _Alloc>::
_M_insert_aux(iterator __pos, const value_type& __x)
{
value_type __x_copy = __x; // XXX copy
#endif
difference_type __index = __pos - this->_M_impl._M_start; //计算总长度
if (static_cast<size_type>(__index) < size() / 2) //前面短 从前面插入
{
push_front(_GLIBCXX_MOVE(front())); //先插入一个front()元素,再把后面的2-index-1 移过来,再加入pos
iterator __front1 = this->_M_impl._M_start;
++__front1;
iterator __front2 = __front1;
++__front2;
__pos = this->_M_impl._M_start + __index;
iterator __pos1 = __pos;
++__pos1;
_GLIBCXX_MOVE3(__front2, __pos1, __front1);
}
else//从后面插
{
push_back(_GLIBCXX_MOVE(back()));//先插入一个back(),再把后面index+1到-2移过来,再插入pos
iterator __back1 = this->_M_impl._M_finish;
--__back1;
iterator __back2 = __back1;
--__back2;
__pos = this->_M_impl._M_start + __index;
_GLIBCXX_MOVE_BACKWARD3(__pos, __back2, __back1);
}
*__pos = _GLIBCXX_MOVE(__x_copy);
return __pos;
}
7 总结
deque是一个两端开口的假的连续空间,为了维持这个假象呢,他就需要付出代价,就是需要自己写迭代器、还要维护一个map,保持连续的状态。头和尾能进行的操作都是一样的,为了维持这样,map像一个端水大师,尽量从中间。