從頭到尾實現deque

關於deque內部數據結構的說明:
deque是能夠同時在收尾進行插入和刪除的數據結構。其表現形式看起來和vector差不多,但是在處理隊頭插入時,其表現的時間複雜度要遠遠地優越於vector。這個得益於deque的內部數據結構地實現。deque使用分段的連續空間組成。爲了管理這個分段的連續空間,所以必須要有一箇中控器來記錄各個分段空間的首地址,然後需要一個長度來記錄該中控器的大小。爲了表現和vector相似,所以需要包含2個迭代器分別用來指向首尾元素。關於迭代器的內存元素:首先要有一個指針指向我們待表示的數據。然後就是該數據在那一個緩衝區上,爲了在外面上表現爲連續的空間,我們必須要記錄該緩衝區的首尾地址,這樣重載++的時候,能夠方便的找到下一塊地址。


其結構示意圖如下:


其代碼實現如下:

//deque的特性:(這段話中別人那copy過來的,因爲感覺寫的還不錯)
//   對於任何一個迭代器i
//     i.node是map array中的某元素的地址. i.node的內容是一個指向某個結點的頭的指針
//     i.first == *(i.node)
//     i.last  == i.first + node_size
//     i.cur是一個指向[i.first, i.last)之間的指針
//       注意: 這意味着i.cur永遠是一個可以解引用的指針,
//            即使其是一個指向結尾後元素的迭代器
//
//   起點和終點總是非奇異(nonsingular)的迭代器.
//     注意: 這意味着空deque一定有一個node, 而一個具有N個元素的deque
//          (N是Buffer Size)一定有有兩個nodes
//
//   對於除了start.node和finish.node之外的每一個node, 每一個node中的元素
//   都是一個初始化過的對象. 如果start.node == finish.node,
//   那麼[start.cur, finish.cur)都是未初始化的空間.
//   否則, [start.cur, start.last)和[finish.first, finish.cur)都是初始化的對象,
//   而[start.first, start.cur)和[finish.cur, finish.last)是未初始化的空間
//
//   [map, map + map_size)是一個合法的非空區間
//   [start.node, finish.node]是內含在[map, map + map_size)區間的合法區間
//   一個在[map, map + map_size)區間內的指針指向一個分配過的node,
//   當且僅當此指針在[start.node, finish.node]區間內


#ifndef ITERATOR_DEQUE_H_INCLUDED
#define ITERATOR_DEQUE_H_INCLUDED
#include"my_iterator_base.h"
namespace juine
{
     //用來決定緩存區的大小
    inline size_t __deque_buf_size(size_t n, size_t sz)
    {
        return n!=0?n:(sz<512?size_t(512/sz):size_t(1));
    }

    template<class T,size_t buf_size=0>
    class Iterator_deque
    {
    public:
        typedef T value_type;
        typedef value_type* pointer;
        typedef value_type& reference;
        typedef ptrdiff_t difference_type;
        typedef random_access_iterator_tag iterator_category;
        typedef T** map_pointer;

        pointer data;  //所要表示節點的位置
        pointer first;  //所在分區的起始位置
        pointer last;   //所在分區的末尾位置
        map_pointer node;  //所在分區

    public:
        size_t buff_size() {  return __deque_buf_size(buf_size,64); }
        //設定節點的位置
        void set_node(map_pointer new_node)
        {
            node=new_node;
            first=0;
            last=0;
            if(new_node!=0)
            {
                first=*new_node;
                last=*new_node+difference_type(buff_size()); //表明又是左閉右開區間
            }
        }
        //construct
        Iterator_deque(pointer _data=0,map_pointer _node=0):data(_data){    set_node(_node);  }
        Iterator_deque(const Iterator_deque& iter):data(iter.data),first(iter.first),last(iter.last),node(iter.node){}
        Iterator_deque& operator=(const Iterator_deque& iter)
        {
            data=iter.data;
            first=iter.first;
            last=iter.last;
            node=iter.node;
            return *this;
        }
        //重載解引用,==,!=
        reference operator*() { return *data;}
        pointer operator->()  { return &(operator*());}
        bool operator==(Iterator_deque iter){ return data==iter.data;}
        bool operator!=(Iterator_deque iter){ return data!=iter.data; }
        bool operator<=(Iterator_deque iter)
        {
            if(node==iter.node)
                return data<=iter.data;
            return node<=iter.node;
        }

        //隨機迭代器必須重載++,--
        Iterator_deque operator++()
        {
            data++;
            if(data==last)
            {
                set_node(node+1);
                data=first;
            }
            return *this;
        }
        Iterator_deque operator++(int)   //後置式的標準寫法
        {
            Iterator_deque iter=*this;
            ++*this;
            return iter;

        }
        Iterator_deque operator--()
        {
            if(data==first)
            {
                set_node(node-1);
                data=last;   //左閉右開
            }
            data--;
            return *this;
        }
        Iterator_deque operator--(int)
        {
            Iterator_deque iter=*this;
            --*this;
            return iter;
        }

////////////////////////////////////////////////////////////////////////////////
// 將迭代器向前移動n個元素, n可以爲負
////////////////////////////////////////////////////////////////////////////////
//                     operator+=(difference_type n)
//                                   ↓
//                      offset = n + (cur - first)
//                                   |
//                                   |---------- offset > 0 ? &&
//                                   |           移動後是否超出當前緩衝區?
//               ----------------------------
//           No  |                          |  Yes
//               |                          |
//               ↓                          |---------- offset > 0?
//           cur += n;                      |
//                              ----------------------------
//                          Yes |                          | No
//                              |                          |
//                              ↓                          |
//                   計算要向後移動多少個緩衝區                |
//                   node_offset =                         |
//                   offset / difference_type              |
//                   (buffer_size());                      ↓
//                              |           計算要向前移動多少個緩衝區
//                              |           node_offset = -difference_type
//                              |           ((-offset - 1) / buffer_size()) - 1;
//                              |                          |
//                              ----------------------------
//                                           |
//                                           |
//                                           ↓
//                                       調整緩衝區
//                              set_node(node + node_offset);
//                                    計算並調整cur指針
////////////////////////////////////////////////////////////////////////////////
    // 以下實現隨機存取。迭代器可以直接跳躍n個距離
        Iterator_deque operator+=(difference_type n)
        {
            difference_type offset=n+(data-first);
            if(offset>=0&&offset<difference_type(buff_size()))
               //目標位置在同一個緩衝區內
                data+=n;
            else
            {
                //目標位置不在同一個緩衝區中
                difference_type node_offset=
                offset>0?offset/difference_type(buff_size()):-difference_type((-offset-1)/buff_size())-1;
                //切換所在緩衝區
                set_node(node+node_offset);
                //切換所在目錄
                data=first+(offset-node_offset*difference_type(buff_size()));
            }
            return *this;
        }
        Iterator_deque operator+(size_t n)
        {
            Iterator_deque iter=*this;
            return iter+=n;
        }
        /*
        自己寫的,發現效率還是比STL的低,而且STL中是將其+=,-=一起處理的
        Iterator_deque operator-=(size_t n)
        {
            if(size_t(data-first)>=n)
                data-=n;
            else
            {
                //表示已經越界了該緩存區。
                set_node(node-1);
                n-=data-first;
                data=last;
                while(n>buff_size())
                {
                    set_node(node-1);
                    n-=buff_size();
                    data=last;
                }
                data-=n;
            }
            return *this;
        }*/
        Iterator_deque operator-=(size_t n)  {  return *this+=-n;}
        Iterator_deque operator-(size_t n)
        {
            Iterator_deque iter=*this;
            iter-=n;
            return iter;
        }

        difference_type operator-(Iterator_deque iter)
        {
            difference_type offet=(node==iter.node)?data-iter.data:((data-first)+(iter.last-iter.data));
            if(node-iter.node>1)
                offet+=(node-iter.node-1)*buff_size();
            return offet;
        }
    };
}


#endif // ITERATOR_DEQUE_H_INCLUDED

容器deque代碼如下:

#ifndef MY_DEQUE_H_INCLUDED
#define MY_DEQUE_H_INCLUDED

#include"iterator_deque.h"
#include<algorithm>
#include<cstdlib>
#include"simple_allocator.h"
using std::cout;
using std::endl;

namespace juine
{

    template<class T,class Alloc=my_alloc,size_t buf_size=0>
    class my_deque
    {
    public:
        typedef T value_type;
        typedef T* pointer;
        typedef T& reference;
        typedef size_t size_type;
        typedef T** map_pointer;

        typedef simple_allocator<T,Alloc>  data_container;
        typedef simple_allocator<pointer,Alloc>  map_container;

        typedef Iterator_deque<T,buf_size> iterator;
    protected:
        size_t map_size;    //申請map_node節點個數,再根據_map能獲得map_node的尾節點
        map_pointer _map;   //_map是申請的map_node節點的首地址
        iterator start;
        iterator finish;

        size_t buff_size() {  return __deque_buf_size(buf_size,64); }
        size_type initial_map_size() {    return 8 ;}
        //構造函數時,將deque的結構安排好,初始化deque數據結構

        void set_null(map_pointer first,map_pointer last)
        {
            //map節點是用來保存緩衝區地起始地址,
            //但是佔爲使用的map節點應該保存NULL
            map_pointer cur;
            for(cur=first;cur<last;cur++)
                *cur=NULL;
        }

        void create_map_and_nodes(size_t n)
        {
            cout<<"buff_size:"<<buff_size()<<endl;
            size_type num_nodes=n/buff_size()+1;//當整除時也是要加1的
            //map中控器的大小
            //map_size=std::max(initial_map_size(),num_nodes+2);
            //爲了測試,map_size特殊化
            map_size=num_nodes;
            _map=map_container::alloc(map_size);

            //以下是令nstart和nfinish指向map所擁有之全部節點的最中央區段
            //保持在最中央,可使頭尾兩端擴充的能量一樣大,每一節點可對應一個緩衝區
            //(出現這種情況是因爲,我們申請的節點數,是大於初始化時所需要的節點數,
            //因此,需要將節點移動中間,使首尾平衡)
            map_pointer nstart=_map+(map_size-num_nodes)/2;
            map_pointer nfinish=nstart+num_nodes-1;
            //申請緩衝區空間,申請節點爲剛纔所找到的平衡位置
            map_pointer cur;
            //申請到的node,暫時不指向緩衝區的應該將其值爲NULL
            set_null(_map,nstart);
            set_null(nfinish,_map+map_size);

            for(cur=nstart;cur<=nfinish;cur++)
                *cur=data_container::alloc(buff_size());

            //接下來是配置迭代器start,finish
            start.set_node(nstart);
            finish.set_node(nfinish);
            start.data=*nstart;
            finish.data=*nfinish+n%buff_size();
        }

        void fill_uninitialized()
        {
            //默認設定20個指針
            _map=map_container::alloc(20);
            map_size=20;
        }
        void fill_uninitialized(size_t n,value_type value)
        {
            //構造deque時,首先是產生deque的結構並安排組織好
            create_map_and_nodes(n);
            /*
            以下注釋部分自己寫的,考慮不充分:自己強制規定map的大小,
            當所需數據剛好爲buf_size時,處理地方式也與STL不一樣
            _map=map_container::alloc(20);
            map_size=20;
            map_pointer temp=_map;
            int remainder=n%buf_size;
            int num=(remainder==0?n/bufsize:(n/buf_size+1));

            while(num)
            {
                pointer p=data_container::alloc(buf_size);
                if(num==1)
                    uninitialized_fill_n(p,remainder==0?buf_size:remainder,value);  //最後一組只能填充remainder個元素
                else
                    uninitialized_fill_n(p,buf_size,value);
                num--;
                *temp=p; //將容器整個結構連接起來
                temp++;
            }
            //對迭代器first,last進行初始化
            iterator temp(*_map,*_map,*_map+buf_size-1,_map);
            first=temp;
            if(remainder!=0)
            {
                iterator temp1(*(_map+n/buf_size)+remainder-1,*(_map+n/buf_size),*(_map+n/buf_size)+remainder-1,_map+n/buf_size );
                last=temp1;
            }
            else
            {
                iterator temp1(*(_map+n/buf_size-1)+buf_size-1,*(_map+n/buf_size),*(_map+n/buf_size-1)+buf_size-1,_map+n/buf_size-1 );
                last=temp1;
            }*/
            //開始對申請到的節點進行初始化
            map_pointer cur;
            for(cur=start.node;cur<finish.node;cur++)
                uninitialized_fill_n(*cur,buff_size(),value);
            uninitialized_fill_n(finish.first,n%buff_size(),value);
        }
        template<class InputIterator>
        void fill_uninitialized(InputIterator first,InputIterator last)
        {
            size_type num=last-first;
            create_map_and_nodes(num);
            //deque結構體已構造好,然後需要賦值
            iterator iter=start;
            while(first!=last)
            {
                construct(iter.data,*first);
                iter++;
                first++;
            }
        }

        //釋放內存
        void dealloc()
        {
            cout<<"deque容器已釋放"<<endl;
            //先遍歷map釋放所有緩衝區
            size_t i;
            for(i=0;i<map_size;i++)
                if(*(_map+i))   //當節點值爲空時,表示其所指向緩衝區爲空,不需要釋放
                {
                    map_pointer temp=_map+i;
                    destroy(*temp,*temp+buff_size());
                    data_container::dealloc(*(_map+i));
                }

            //刪除_map
            map_container::dealloc(_map);
        }
    public:
        //construct
        my_deque(){    fill_uninitialized();}
        my_deque(size_t n,value_type value) { fill_uninitialized(n,value);}
        template<class InputIterator>
        my_deque(InputIterator first,InputIterator last){ fill_uninitialized(first,last);}
        ~my_deque(){ dealloc() ;}

        iterator begin(){   return start; }
        iterator end(){     return finish; }
        reference operator[](size_type n)
        {
            iterator iter=start+n;
            return *iter;
        }
        reference at(size_type n)
        {
            iterator iter=start+n;
            if(finish<=iter)
            {
                cout<<"out of memory"<<endl;
                exit(1);
            }
            return iter;
        }
        reference front(){  return *start; }
        reference back(){   return *(finish-1); }
        size_type size() {  return finish-start; }
        bool empty(){   return start==finish; }

        void push_front(value_type value){  insert(start,value); }
        void push_back(value_type value){   insert(finish,value);}

        //插入函數(考慮緩衝區大小夠不,Map大小夠不,還有效率-左移還是右移)
        void insert(iterator position,value_type value)
        {
            if(position-start<=finish-position)  //表示需要左移動
            {
                iterator temp,new_start;
                temp=new_start=insert_aux_left(1,position);
                iterator temp1=start;
                //這個地方賦值,利用了迭代器重載函數++,使其能夠自動在片段連續空間上移動
                while(temp1!=position)  //移動元素
                    *temp++=*temp1++;
                *temp=value;  //最後插入元素
                start=new_start;   //插入之後,迭代器要更新,Temp保存的就是最新的位子
            }
            else
            {
                iterator temp,new_finish;
                temp=new_finish=insert_aux_right(1,position);
                iterator temp1=finish;
                //這個地方賦值,利用了迭代器重載函數++,使其能夠自動在片段連續空間上移動
                while(temp1!=position)  //移動元素
                    *--temp=*--temp1;
                *position=value;  //最後插入元素
                finish=new_finish;   //插入之後,迭代器要更新,Temp保
            }

        }

        template<class InputIterator>
        void insert(iterator position,InputIterator first,InputIterator last)
        {
            int n=last-first;
            if(position-start<=finish-position)  //表示需要左移動
            {
                iterator temp,new_start;
                temp=new_start=insert_aux_left(n,position);
                iterator temp1=start;
                //這個地方賦值,利用了迭代器重載函數++,使其能夠自動在片段連續空間上移動
                while(temp1!=position)  //移動元素
                    *temp++=*temp1++;
                while(temp!=position)
                    *temp++=*first++;  //最後插入元素
                start=new_start;
            }
            else
            {
                iterator temp,new_finish;
                temp=new_finish=insert_aux_right(n,position);
                iterator temp1=finish;
                //這個地方賦值,利用了迭代器重載函數++,使其能夠自動在片段連續空間上移動
                while(temp1!=position)  //移動元素
                    *--temp=*--temp1;
                while(first!=last)
                    *position++=*first++;  //最後插入元素
                finish=new_finish;
            }
        }

        //該函數功能:如容量不夠,則擴充容量然後更新該deque結構
        //該函數返回值,是表示在插入後,start迭代器所在正確位置
        iterator insert_aux_left(size_t number,iterator& position)    //number表示插入的數據,而且該數據是左移的。
        {
            iterator result;
            if(start.data-number<=start.first-1)  //表示前端緩衝區已滿,因此要增加緩衝區
            {
                //要增加的緩衝區
                int left_need_size=number-(start.data-start.first);
                int need_map_nodes=left_need_size/buff_size()+1;
                if(start.node-_map<need_map_nodes)  //表示中控器前端node節點不夠了
                {
                    //雖然前面的不夠,但是整個_map空餘空間夠,可以通過移動_map節點來實現
                    //不用重新分配新的節點。
                    if(size_t(finish.node-start.node+1+need_map_nodes)<=map_size)
                    {
                        cout<<"移動map達到要求"<<endl;
                       //offset爲map移動偏移量
                        int offset=need_map_nodes-(start.node-_map);
                        map_pointer temp=finish.node;
                        while(temp>=start.node)
                        {
                            *(temp+offset)=*temp;
                            temp--;
                        }
                        //前面_map移動後,finish中所存的Node,已經不是最後一個節點所在位置
                        //故要移動,更新finish
                        finish.node=finish.node+offset;
                        //map前端node節點已夠用,現在是要申請緩衝區內存
                        map_pointer malloc_node=_map;
                        while(malloc_node<(start.node+offset))
                        {
                            *malloc_node=data_container::alloc(buff_size());
                            malloc_node++;
                        }
                        result.set_node(_map);
                        result.data= *_map+buff_size()-left_need_size%buff_size();
                    }
                    else //map節點不夠,需要重新分配其map_nodea
                    {
                        //map大小,簡單以2倍來擴充
                        map_pointer new_malloc_map=map_container::alloc(map_size*2);
                        map_pointer item=new_malloc_map+map_size/2;
                        map_pointer old_node=start.node;

                        set_null(new_malloc_map,item);
                        set_null(new_malloc_map+map_size,new_malloc_map+map_size*2);

                        //將舊的map複製過來
                        while(old_node<=finish.node)
                            *item++=*old_node++;
                        //因爲map重新申請了,所以position也應該換到新map對應的位置上
                        position.node=new_malloc_map+map_size/2+(position.node-start.node);

                        //刪除舊的map,釋放內存
                        map_container::dealloc(_map);
                        start.node=new_malloc_map+map_size/2;
                        finish.node=item-1;
                        _map=new_malloc_map;
                        map_size*=2;


                         //需要申請內存
                        map_pointer new_node=start.node-1;
                        while(new_node>=start.node-need_map_nodes)
                        {
                            *new_node=data_container::alloc(buff_size());
                            new_node--;
                        }
                        result.set_node(start.node-need_map_nodes);
                        result.data= *result.node+buff_size()-left_need_size%buff_size();


                    }
                }
                else //緩衝區不夠,但是map前端所需node是夠的
                {
                    //需要申請內存
                    map_pointer new_node=start.node-1;
                    while(new_node>=start.node-need_map_nodes)
                    {
                        *new_node=data_container::alloc(buff_size());
                        new_node--;
                    }
                    result.set_node(start.node-need_map_nodes);
                    result.data= *result.node+buff_size()-left_need_size%buff_size();

                }
            }
            else //緩衝區是夠的
            {
                result.data=start.data-number;
                result.set_node(start.node);
            }
            return result;
        }

        iterator insert_aux_right(size_t number,iterator& position)    //number表示插入的數據,而且該數據是右移的。
        {
            iterator result;
            if(finish.data+number>=finish.last)  //表示前端緩衝區已滿,因此要增加緩衝區
            {
                //要增加的緩衝區
                int left_need_size=number-(finish.last-finish.data-1);
                int need_map_nodes=left_need_size/buff_size()+1;
                if(_map+map_size-finish.node<need_map_nodes)   //表示中控器後端node節點不夠了
                {
                    //雖然前面的不夠,但是整個_map空餘空間夠,可以通過移動_map節點來實現
                    //不用重新分配新的節點。
                    if(size_t(finish.node-start.node+1+need_map_nodes)<=map_size)
                    {
                        cout<<"移動map達到要求"<<endl;
                        //offset爲map移動偏移量
                        int offset=need_map_nodes-(_map-finish.node);
                        map_pointer temp=start.node;
                        while(temp<=finish.node)
                        {
                            *(temp-offset)=*temp;
                            temp++;
                        }
                        //前面_map移動後,finish中所存的Node,已經不是最後一個節點所在位置
                        //故要移動,更新finish
                        start.node=start.node-offset;
                        //map前端node節點已夠用,現在是要申請緩衝區內存
                        map_pointer malloc_node=_map+map_size;
                        while(malloc_node>(finish.node-offset))
                        {
                            *malloc_node=data_container::alloc(buff_size());
                            malloc_node--;
                        }
                        result.set_node(_map+map_size);
                        result.data= *(_map+map_size)+left_need_size%buff_size();
                    }
                    else //map節點不夠,需要重新分配其map_nodea
                    {
                        //map大小,簡單以2倍來擴充
                        map_pointer new_malloc_map=map_container::alloc(map_size*2);
                        map_pointer item=new_malloc_map+map_size/2;
                        map_pointer old_node=start.node;

                        set_null(new_malloc_map,item);
                        set_null(new_malloc_map+map_size,new_malloc_map+map_size*2);

                        //將舊的map複製過來
                        while(old_node<=finish.node)
                            *item++=*old_node++;
                        //因爲map重新申請了,所以position也應該換到新map對應的位置上
                        position.node=new_malloc_map+map_size/2+(position.node-start.node);

                        //刪除舊的map,釋放內存
                        map_container::dealloc(_map);
                        start.node=new_malloc_map+map_size/2;
                        finish.node=item-1;
                        _map=new_malloc_map;
                        map_size*=2;

                         //需要申請內存
                        map_pointer new_node=finish.node+1;
                        while(new_node<=finish.node+need_map_nodes)
                        {
                            *new_node=data_container::alloc(buff_size());
                            new_node++;
                        }
                        result.set_node(finish.node+need_map_nodes);
                        result.data= *result.node+left_need_size%buff_size();


                    }
                }
                else //緩衝區不夠,但是map前端所需node是夠的
                {
                    //需要申請內存
                    map_pointer new_node=finish.node+1;
                    while(new_node<=finish.node+need_map_nodes)
                    {
                        *new_node=data_container::alloc(buff_size());
                        new_node++;
                    }
                    result.set_node(finish.node+need_map_nodes);
                    result.data= *result.node+left_need_size%buff_size();

                }
            }
            else //緩衝區是夠的
            {
                result.data=finish.data+number;
                result.set_node(finish.node);
            }
            return result;
        }

        //刪除元素(爲了簡單,只寫一種)
        void erase(iterator position)
        {
            iterator temp;
            if(position-start<=finish-position)  //左邊的元素右移動
            {
                temp=position-1;
                while(start<=temp)
                {
                    *(temp+1)=*temp;
                    temp--;
                }
                start++;
                if((start-1).node!=start.node)
                    *(start-1).node=NULL;


            }
            else
            {
                temp=position+1;
                while(temp!=finish)
                {
                    *(temp-1)=*temp;
                    temp++;
                }
                finish--;
                if((finish+1).node!=finish.node)
                    *(finish+1).node=NULL;
            }
        }
        void pop_back(){ erase(finish-1);}
        void pop_front(){ erase(start);   }
    };
}

#endif // MY_DEQUE_H_INCLUDED
測試代碼如下:

#include<iostream>
#include<cstdlib>
#include<vector>
#include"my_deque.h"
using namespace std;
using namespace juine;
int main()
{
    int a[9]={1,2,3,4,5,6,7,8,9};
    my_deque<int> tt(a,a+9);
    tt.front()+=tt.back();
    tt.back()+=tt.front();
    my_deque<int>::iterator iter=tt.begin()+2;
    tt.insert(iter,a,a+9);
    tt.insert(tt.begin(),11);
    tt.push_front(12);
    tt.push_back(25);
    tt.erase(tt.begin()+4);
    tt.pop_front();

    for(iter=tt.begin();iter!=tt.end();iter++)
        cout<<*iter<<endl;
    cout<<"size:"<<tt.size()<<endl;
    return 0;
}

測試結果爲:


已基本達到要求,下一章實現序列容器。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章