zoukankan      html  css  js  c++  java
  • deque

          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;  
    }  
  • 相关阅读:
    LeetCode——打家劫舍问题
    LeetCode——无重叠区间
    LeetCode——岛屿数量
    LeetCode——石子游戏
    LeetCode——最长回文子序列
    LeetCode——高层扔鸡蛋*
    LeetCode——零钱兑换
    LeetCode刷题之BFS和DFS
    没有学历,四步加入谷歌
    坚持编程
  • 原文地址:https://www.cnblogs.com/daocaorenblog/p/5301283.html
Copyright © 2011-2022 走看看