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;
}
測試結果爲:
已基本達到要求,下一章實現序列容器。