zoukankan      html  css  js  c++  java
  • STL容器之deque数据结构解析

    今天我们来看deque这个数据结构。

      我们在C语言的数据结构之中,应该是没有deque这个数据结构的,但是我们肯定有两个数据结构,一个叫做quene(就是所谓的队列),还有一个叫做stack(也就是所谓栈),当然对于我们来说应该是自己写出来的,但是在c++当中他们两个的实现就不一样了,他们采用同一个底层数据结构叫做deque的来进行扩充。

      deque这个结构体可以说是list和vector的一个混合结构,所以我们也不能说他是连续空间还是非连续空间。但是起码在应用层(也就是我们看到的这层),他是一个连续空间(因为可以随机跳转)。那么接下来我们就说一下这个deque在源码当中的实现。

      一、为什么要用deque

      二、Deque是什么

      三、Deque的源码实现

      四、Deque迭代器的实现

      五、deque中包含的各种函数说明和软件架构

      六、Deque的优点和缺点

      一、为什么要用deque

        在C语言中我们有很多的数据结构,比如队列,链表等等。这些大部分都在STL容器当中实现了,但是队列和栈我们始终没有实现。所以deque就出现了。

      二、Deque是什么

        Deque的用途既包含了数据结构中队列的功能,也包含了栈的功能(因为他是两端都可以插入的),当然我们说list也可以两端插入的,不过list不支持随机读取,但是Deque可以支持随机查找,但是它的插入效率要低于vector(这也好理解,因为判断的东西多了嘛)。

      三、deque的源码实现

        首先我们来看一下deque的内存模型:

       我们来分析一下deque的内存模型。首先deque中包含一个map(注意,这个map不是STL中的map),他是一个指针数组,里面存放了指针,每个指针中指向了一段内存,而这段内存才是真正存放数据的地方。所以,deque当中必须包含两个迭代器指针,分别是start和finish这两个迭代器,每个迭代器中又包含了4个指针,分别是start,finish,current以及size这四个部分。所以,一个空的deque应该是有16个指针的。

      这里说一下,对于源码来说,我们使用的时候,比如*iterator这个函数,迭代器会自动把当前数据放到cur当中,非常方便,但是这个要花点时间,因为毕竟函数重载了嘛。

      好的,接下来我们说一下deque的源码实现。deque的源码部分我们可以分成三个部分:

        一、初始化部分

        二、insert部分(包括头部添加,尾部添加,中间添加)

        三、删除部分

        一、初始化部分

          我们首先来看第一部分初始化部分

            

    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);

          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_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;

          }

          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)));

        }

    这里我要说明的部分有两点:

      首先是这句话:

    this->_M_impl._M_map_size = std::max((size_t) _S_initial_map_size,

                                     size_t(__num_nodes + 2));

      这句话指定的是一开始map中节点的个数。我们可以看到,map所用的是初始化节点和我们指定的节点的最大值,而初始化节点大小是8,所以map中节点最少是8,或者是我们指定节点的数字加2,为什么要加2呢?因为头部和尾部各加一个,这样比较方便。

      然后这里是第二句话:

    _Map_pointer __nstart = (this->_M_impl._M_map

                             + (this->_M_impl._M_map_size - __num_nodes) / 2);

      这句话指定的是头节点nstart所使用的节点数。我们注意,deque的start不是在开头,而是在中间,为什么要这么设计呢?诚如之间所说,deque本身是两头扩充的,这样做比较方便。

      接下来我们来看一下inert的部分

        我们以push_back为例:

     push_back(bool __x)

          {

      if (this->_M_impl._M_finish._M_p != this->_M_impl._M_end_addr())

        *this->_M_impl._M_finish++ = __x;

      else

        _M_insert_aux(end(), __x);

          }

    这个代码比较好理解,就是如果说没到节点的尾端,那么直接插入,否则的话执行_M_insert_aux这个函数

    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()));

            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()));

            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;

      它的这个插入是很有趣的,它可以这样看,首先是对其进行赋值(拷贝一下),然后我们判断是否当前的插入位置在前半段还是后半段,这是为什么呢?我们接着往后看,如果是前半段,那么我们push_front这个函数,然后将他移动到中间。这就明白了吧,这样可以减少移动次数。

      四、Deque迭代器的使用

        我们这里以operator++举例

        

     _Self&

          operator++() _GLIBCXX_NOEXCEPT

          {

          ++_M_cur;

          if (_M_cur == _M_last)

            {

              _M_set_node(_M_node + 1);

              _M_cur = _M_first;

            }

          return *this;

          }

    我们看到,如果当前节点不到last的时候M_cur正常++,反之则要跳到下一个节点。我们继续看

      

    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());

          }

    再这里,我们跳入了下一个节点,并且重新将M_first,last重新定义,并且再返回时候将first赋值给了current

      以上就是deque的源码,今天就分享到这了。

  • 相关阅读:
    【NOIP2007】守望者的逃离
    20200321(ABC)题解 by 马鸿儒 孙晨曦
    20200320(ABC)题解 by 王一帆
    20200319(ABC)题解 by 王一帆 梁延杰 丁智辰
    20200314(ABC)题解 by 董国梁 蒋丽君 章思航
    20200309(ABC)题解 by 梁延杰
    20200307(DEF)题解 by 孙晨曦
    20200306(ABC)题解 by 孙晨曦
    20200305(DEF)题解 by 孙晨曦
    20200303(ABC)题解 by 王锐,董国梁
  • 原文地址:https://www.cnblogs.com/songyuchen/p/14357521.html
Copyright © 2011-2022 走看看