在使用c++容器的時候其底層如何實現 例如 vector 容器 :是一個內存可以二倍擴容的向量容器,使用方便但是對內存要求嚴格,弊端明顯 list 容器 : 雙向循環鏈表 deque 容器 :雙端隊列
deque容器是C++標准模版庫(STL,Standard Template Library)中的部分內容。deque容器類與vector類似,支持隨機訪問和快速插入刪除,它在容器中某一位置上的操作所花費的是線性時間。與vector不同的是,deque還支持從開始端插入數據:push_front()。
實際上雙端隊列是一個二維數組,但是實際存儲數據的部分並不是連續的,一維數組存放指針,指向二維申請出來的空間,如圖
首先申請一維空間存放指向二維的指針,例如一維空間長度為int len;則在len/4的位置先申請一塊二維空間,指向前的指針*_frist與指向后的指針*_last 位於二維空間的中間,前插數據則*_frist--,后插數據則*_last++;
如果前插到二維的0下標位置,若一維數組上一個位置指向的空間為空,此時會在一維上一個位置申請二維,*_frist指向二維尾部,例如上圖,會在*p 0 的位置申請二維,*_frist指向新開辟的二維的尾部,實現繼續前插,若上面的所有一維都申請了二維,但是下面還有一維數組還有空,則整體將數據往下挪,把一維0號下標的二維空出來,繼續前插,直到所有的一維都申請了二維並且數據放滿,此時需要擴容,將一維數組2倍擴大,然后將所有數據移到新的一維數組所指向的二維數組中,滿足數據位置為 :(新的一維數組長度/4)的位置。尾插同理 只是向下插方向相反。
下面是數據結構和基本函數
#define QUE_SIZE(T) 4096 / sizeof(T) #define DEFAULT_QUE 2 class Deque { public: Deque(); //構造函數 ~Deque(); // 析構函數 Deque(const Deque &src); // 左值引用參數構造拷貝函數 防止淺拷貝 Deque(Deque &&src); // 右值引用參數拷貝構造函數 防止臨時量對空間時間的浪費 Deque& operator=(const Deque &src); // 左值引用參數賦值構造函數 Deque& operator=(Deque &&src); // 帶右值引用參數的賦值構造函數 void push_back(int val); // 尾部入隊 void pop_back(); // 尾部刪除 void push_front(int val); // 前插 void pop_front(); //前刪 int front(); //返回隊頭元素 int back(); // 返回隊尾元素 bool empty(); // 是否對空 private: int **mMapper; int *mFirst; int *mLast; int mMapperSize; };
具體函數 封裝成類
#include<iostream> #include<stdlib.h> #include<string.h> using namespace std; #define QUE_SIZE(T) 4096 / sizeof(T) #define DEFAULT_QUE 2 class Deque { public: Deque() { mMapper=new int*[DEFAULT_QUE](); mMapperSize=DEFAULT_QUE; mMapper[DEFAULT_QUE/4]=new int[QUE_SIZE(int)]; mFirst=mMapper[DEFAULT_QUE/4]+QUE_SIZE(int)/2; mLast=mFirst+1; } ~Deque() { int i=0; for(;i<mMapperSize;i++) { delete []mMapper[i]; mMapper[i]=nullptr; } delete []mMapper; mMapper=nullptr; } Deque(const Deque &src) { mMapper=new int*[src.mMapperSize](); int mfirst=-1; int mlast=-1; bool sign=1; int i; for(i=0;i<src.mMapperSize;i++) { if(src.mMapper[i]==nullptr) { continue; } if(src.mMapper[i]!=nullptr) { if(sign) { mfirst=i; sign=0; } int j; mMapper[i]=new int[QUE_SIZE(int)]; for(j=0;j<QUE_SIZE(int);j++) { *(mMapper[i]+j)=*(src.mMapper[i]+j); } mlast=i; } } mFirst=mMapper[mfirst]+(src.mFirst-src.mMapper[mfirst]); mLast=mMapper[mlast]+(src.mLast-src.mMapper[mlast]); mMapperSize=src.mMapperSize; } Deque(Deque &&src) { mMapper=src.mMapper; mMapperSize=src.mMapperSize; mFirst=src.mFirst; mLast=src.mLast; src.mMapper=nullptr; } Deque& operator=(const Deque &src) { if(this==&src) { return *this; } int i=0; for(;i<mMapperSize;i++) { delete[]mMapper[i]; } delete []mMapper; mMapper=new int*[src.mMapperSize](); int mfirst=-1; int mlast=-1; bool sign=-1; for(i=0;i<src.mMapperSize;i++) { if(src.mMapper[i]==nullptr) { continue; } if(src.mMapper[i]!=nullptr) { if(sign) { mfirst=i; sign=0; } int j; mMapper[i]=new int[QUE_SIZE(int)]; for(j=0;j<QUE_SIZE(int);j++) { *(mMapper[i]+j)=*(src.mMapper[i]+j); } mlast=i; } } mFirst=mMapper[mfirst]+(src.mFirst-src.mMapper[mfirst]); mLast=mMapper[mlast]+(src.mLast-src.mMapper[mlast]); mMapperSize=src.mMapperSize; return *this; } Deque& operator=(Deque &&src) { int i=0; for(;i<mMapperSize;i++) { delete []mMapper[i]; } delete []mMapper; mMapper=src.mMapper; mMapperSize=src.mMapperSize; mLast=src.mLast; mFirst=src.mFirst; src.mMapper=nullptr; return *this; } void push_back(int val) // 尾部入隊 { int index = -1; for (int i = 0; i < mMapperSize; ++i) { if (mMapper[i] == nullptr) { index = i; continue; } // 表示last已經指向行的末尾了,需要擴容 if (mMapper[i] + QUE_SIZE(int) == mLast) { // 說明下面還有空行,直接分配新的第二維數組 if (i != mMapperSize - 1) { mMapper[i+1] = new int[QUE_SIZE(int)]; mLast = mMapper[i + 1]; break; } // 說明last下面已經沒有空閑行了 if (index != -1) { // 說明上面還有空閑行,整體往上挪一行,下面就有一個空閑行了 for (int i = index; i < mMapperSize-1; ++i) { mMapper[i] = mMapper[i + 1]; } mMapper[mMapperSize-1] = new int[QUE_SIZE(int)]; mLast = mMapper[mMapperSize - 1]; break; } else { // 說明上面沒有空閑行,一維需要開始擴容了 int **tmpMapper = new int*[2* mMapperSize]; int idx = 2 * mMapperSize / 4; for (int i = 0; i < mMapperSize; ++i) { tmpMapper[idx++] = mMapper[i]; } delete[]mMapper; mMapper = tmpMapper; mMapperSize *= 2; mMapper[idx] = new int[QUE_SIZE(int)]; mLast = mMapper[idx]; break; } } } // 添加元素 *mLast++ = val; } void pop_back() { int i; bool sign=1; for(i=0;i<mMapperSize;i++) { if(mMapper[i]==mLast) { mLast=mMapper[i-1]+QUE_SIZE(int); sign=0; break; } } if(sign) { mLast--; } } void push_front(int val) { // 遍歷第一維的數組,從下往上遍歷 與尾插方法一樣 int index = -1; int i=mMapperSize-1; for (; i>=0; i--) { if (mMapper[i] == nullptr) { index = i; continue; } // 表示frist已經指向行首,需要擴容 if (mMapper[i] ==mFirst) { // 說明上面還有空行,直接分配新的第二維數組 if (i != 0) { mMapper[i-1] = new int[QUE_SIZE(int)]; mFirst = mMapper[i-1]+QUE_SIZE(int); break; } // 說明frist下面已經沒有空閑行了 if (index != -1) { // 說明下面還有空閑行,整體往下挪一行,上面就有一個空閑行了 for (i = index; i >0; i--) { mMapper[i] = mMapper[i-1]; } mMapper[0] = new int[QUE_SIZE(int)]; mFirst= mMapper[0]+QUE_SIZE(int); break; } else { // 說明下面沒有空閑行,一維需要開始擴容了 int **tmpMapper = new int*[2* mMapperSize]; int idx = 2 * mMapperSize / 4; for (int i = 0; i < mMapperSize; ++i) { tmpMapper[idx+i] = mMapper[i]; } delete[]mMapper; mMapper = tmpMapper; mMapperSize *= 2; mMapper[idx-1] = new int[QUE_SIZE(int)]; mFirst = mMapper[idx-1]+QUE_SIZE(int); break; } } } // 添加元素 *mFirst-- = val; } void pop_front() { int i; bool sign=1; for(i=0;i<mMapperSize;i++) { if(mMapper[i]+QUE_SIZE(int)==mFirst) { mFirst=mMapper[i+1]; sign=0; break; } } if(sign) { mFirst++; } } int front() { int i; for(i=0;i<mMapperSize;i++) { if(mMapper[i]+QUE_SIZE(int)==mFirst) { return *mMapper[i+1]; } } return *(mFirst+1); } int back() { int i=0; for(;i<mMapperSize;i++) { if(mMapper[i]==mLast) { return *(mMapper[i-1]+QUE_SIZE(int)); } } return *(mLast-1); } bool empty() { return mFirst+1==mLast; } private: int **mMapper; int *mFirst; int *mLast; int mMapperSize; }; int main() { Deque s1; Deque s2; int i=0; for(;i<10000;i++) { s1.push_back(i); } s2=s1; for(i=0;i<10000;i++) { cout<<s2.front()<<' '; s2.pop_front(); } cout<<endl; return 0; }