stl中的vector是競賽中常用的容器,原因在於省內存,$O(1)$ 在后端插入和刪除、隨機下標訪問,今天就來談談它的實現。
最簡單的一個動態數組
動態數組並不是真正意義上的動態的內存,而是一塊連續的內存,當添加新的元素時,容量已經等於當前的大小的時候(存不下了),執行下面3步
- 重新開辟一塊大小為當前容量兩倍的數組
- 把原數據拷貝過去
- 釋放掉舊的數組
完事后再把這個元素加在后面。
那么你一定會很好奇,它為什么會是 $O(1)$。這個是均攤下來的結果,我們只需要證明總共拷貝的元素個數是O(n)級別的就好了(因為釋放內存和申請內存都比較快,詳情自行百度吧)。
總共拷貝的元素個數是 $1 + 2 + 4 + \cdots+ 2^{\lfloor \log_2 n \rfloor}$ ,根據等比數列求和公式那么有這個和等於 $2^{\lfloor \log_2 n \rfloor + 1} - 1$ 。因為 $2^{\lfloor \log_2 n \rfloor + 1} - 1 < 2^{log_2 n + 1} = 2n$,因此總共拷貝的元素個數是 $O(n)$ 個,均攤下來,每次在尾部插入的時間復雜度就是 $O(1)$。
1 #include<iostream> 2 #include<malloc.h> 3 using namespace std; 4 5 template<typename T> 6 class Vector { 7 protected: 8 int cap; 9 int siz; 10 T* l; 11 public: 12 Vector():l(NULL), cap(0), siz(0) { } 13 14 inline void push_back(T x) { 15 if(l == NULL) { 16 l = new T[4]; 17 cap = 4, siz = 0; 18 } 19 if(siz == cap) { 20 l = (T*)realloc(l, sizeof(T) * cap * 2); //重新申請內存,並拷貝數組 21 cap = cap << 1; 22 } 23 l[siz++] = x; 24 } 25 26 T& operator [] (int pos) { 27 return l[pos]; 28 } 29 30 inline int size() { 31 return siz; 32 } 33 34 inline int capt() { 35 return cap; 36 } 37 };
這里為了省時間,不用new而是用malloc,這樣稍微會快一些。
支持在頭部插入的動態數組
stl的vector是不支持push_front,但是你可以通過insert在頭部插入,但是這樣是O(n)的,而不是O(1)的,因為vector中的insert是將它后面的內容往后copy,再把自己弄進vector。但是顯然可以用近似的方法實現push_front,只是多需要一些內存罷了。
開一個頭部緩沖數組,它的大小是l的一半,凡是push_front,都把加入的元素塞進去。當這個數組滿了或者重新申請內存時,就先將它中的內容拷貝進新的數組。其他地方就稍微改一下就好了。因為這樣會消耗更多的內存,所以用了一個boolean類型的變量sign區分是否需要使它實現push_front。
1 #include<iostream> 2 #include<algorithm> 3 #include<string.h> 4 #include<malloc.h> 5 using namespace std; 6 typedef bool boolean; 7 8 template<typename T> 9 class Vector { 10 protected: 11 int cap; //容量 12 int siz; //當前元素個數 13 boolean sign; //是否支持push_front 14 T* l; //數組部分 15 T* fronts; //頭部緩沖數組(追加在前面的部分) 16 int sizf; //前面部分的大小 17 18 inline void extends(int newsize) { 19 if(sign) { 20 T* newarr = (T*)malloc(sizeof(T) * newsize); //申請新的數組 21 memcpy(newarr, fronts, sizeof(T) * sizf); //將原來添加到前面的數據拷貝進這個數組 22 reverse(newarr, newarr + sizf); //翻轉這一段,因為往前塞時卻是往后存 23 memcpy(newarr + sizf, l, sizeof(T) * siz); //拷貝從l開始的這一段 24 siz += sizf, cap = newsize; //更新數據 25 free(l); //釋放指針指向的數組 26 free(fronts); 27 sizf = 0; //重新設置大小 28 fronts = (T*)malloc(sizeof(T) * (newsize >> 1)); //重新設置動態數組頭部緩沖數組 29 l = newarr; 30 } else { 31 l = (T*)realloc(l, sizeof(T) * newsize); 32 cap = newsize; 33 } 34 } 35 public: 36 Vector():l(NULL), cap(0), siz(0), sizf(0), sign(false), fronts(NULL) { } 37 Vector(boolean sign):l(NULL), cap(0), siz(0), sizf(0), sign(sign), fronts(NULL) { } 38 39 /** 40 * 向動態數組尾部追加一個元素。 41 * @param x 追加的元素 42 * @return 如果成功追加,返回true,否則返回false 43 */ 44 inline boolean push_back(T x) { 45 if(l == NULL) { 46 extends(4); 47 if(l == NULL) return false; 48 } 49 if(siz == cap) { 50 extends(cap * 2); 51 if(l == NULL) return false; 52 } 53 l[siz++] = x; 54 return true; 55 } 56 57 /** 58 * 向動態數組頭部追加一個元素。 59 * @param x 追加的元素 60 * @return 如果成功追加,返回true,否則返回false 61 */ 62 inline boolean push_front(T x) { 63 if(!sign) return false; 64 if(fronts == NULL) { 65 extends(4); 66 if(fronts == NULL) return false; 67 } 68 if(sizf == (cap >> 1)) { 69 extends(cap * 2); 70 if(fronts == NULL) return false; 71 } 72 fronts[sizf++] = x; 73 return true; 74 } 75 76 T& operator [] (int pos) { 77 if(pos < sizf) 78 return fronts[sizf - pos - 1]; 79 return l[pos - sizf]; 80 } 81 82 inline int size() { 83 return siz + sizf; 84 } 85 86 inline int capt() { 87 if(sign) return cap + (cap >> 1); 88 return cap; 89 } 90 91 inline int size_front() { 92 return sizf; 93 } 94 95 inline int size_middle() { 96 return siz; 97 } 98 };
支持在雙端刪除的動態數組
如果push_front還用上面的方法實現,那么代碼可能會很長,因為在頭部刪除插入都需要特判。所以考慮將頭部緩沖數組和l合並到一起。這樣不難想到隊列,給一個頭指針和一個尾指針也可以實現邊界判斷(是否需要申請空間了)。
為了不被極端數據卡死,每次重新申請空間時,都將舊的數組拷貝到新的數組的中間。
下面來說一下刪除。隊列的刪除很簡單,挪"指針"就好了, 只不過要考慮下面這個問題
要不要釋放內存?
至少我覺得應該是,這樣不浪費內存空間。但是不是當當前元素個數少於容量的一半就應該縮小當前數組,而是少於四分之一時才考慮縮小,不然可能有些不良心的題目數據讓你刪完一個,然后又插入一個,迫使你重新申請內存導致時間復雜度變成了 $O(n)$。
考慮證明當只支持 push_back 時的復雜度,假設上一次重構后的大小為 $l$,那么至少有 $\frac{l}{2}$ 個位置存在元素,再次因為刪除發生重構時,至少刪除了 $\frac{l}{4}$ 的元素,花費的時間是 $O(l)$,因此均攤每次刪除是 $O(1)$ 的。
1 #include<iostream> 2 #include<algorithm> 3 #include<string.h> 4 #include<malloc.h> 5 using namespace std; 6 typedef bool boolean; 7 8 template<typename T> 9 class Vector { 10 protected: 11 int cap; //容量 12 boolean sign; //是否支持push_front 13 T* l; //數組部分 14 int front; //頭指針 15 int rear; //尾指針 16 17 inline void extends(int newsize) { 18 if(sign) { 19 int realsize = newsize * 3; //實際大小 20 T* newarr = (T*)malloc(sizeof(T) * realsize); //開辟新的數組 21 int s = this->size(); 22 int start_pos = (realsize - s) >> 1; //將原數據拷貝到新數組的中間 23 memcpy(newarr + start_pos, l + front + 1, sizeof(T) * s); 24 free(l); //釋放原數組 25 l = newarr; 26 front = start_pos - 1, rear = start_pos + s; //重新計算下標 27 cap = newsize; 28 } else { 29 l = (T*)realloc(l, sizeof(T) * newsize); 30 cap = newsize; 31 } 32 } 33 34 inline void construct(boolean sign) { 35 if(!sign) { 36 l = (T*)malloc(sizeof(T) * 4); 37 front = -1, rear = 0, cap = 4; 38 } else { 39 l = (T*)malloc(sizeof(T) * 6); 40 front = 1, rear = 2, cap = 2; 41 } 42 } 43 public: 44 Vector():l(NULL), front(-1), rear(0), sign(false) { } 45 Vector(boolean sign):l(NULL), front(-1), rear(0), sign(sign) { } 46 47 /** 48 * 向動態數組尾部追加一個元素。 49 * @param x 追加的元素 50 * @return 如果成功追加,返回true,否則返回false 51 */ 52 inline boolean push_back(T x) { 53 if(l == NULL) { 54 construct(sign); 55 if(l == NULL) return false; 56 } 57 if(!sign && rear == cap) { 58 extends(cap * 2); 59 if(l == NULL) return false; 60 } else if(sign && rear == cap * 3) { 61 extends(cap * 2); 62 if(l == NULL) return false; 63 } 64 l[rear++] = x; 65 return true; 66 } 67 68 /** 69 * 向動態數組頭部追加一個元素。 70 * @param x 追加的元素 71 * @return 如果成功追加,返回true,否則返回false 72 */ 73 inline boolean push_front(T x) { 74 if(!sign) return false; 75 if(l == NULL) { 76 construct(sign); 77 if(l == NULL) return false; 78 } 79 if(front == -1) { 80 extends(cap * 2); 81 if(l == NULL) return false; 82 } 83 l[front--] = x; 84 return true; 85 } 86 87 /** 88 * 在頭部刪除動態數組的一個元素。 89 * @return 如果成功刪除,返回true,否則返回false 90 */ 91 inline boolean pop_front() { 92 if(!sign) return false; 93 if(front == rear - 1) return false; 94 front++; 95 int s = this->size(); 96 int c = this->capt(); 97 if(s < (c >> 2) && c >= 12) { //當當前容量過大時,縮小一下容量 98 extends(cap >> 1); 99 } 100 return true; 101 } 102 103 /** 104 * 在尾部刪除動態數組的一個元素 105 * @return 如果成功刪除,返回true,否則返回false 106 */ 107 inline boolean pop_back() { 108 if(front == rear - 1) return false; 109 rear--; 110 int s = this->size(); 111 int c = this->capt(); 112 if(s < (c >> 2) && c >= 12) { //當當前容量過大時,縮小一下容量 113 extends(cap >> 1); 114 } 115 return true; 116 } 117 118 T& operator [] (int pos) { 119 return l[front + pos + 1]; 120 } 121 122 inline int size() { 123 return rear - front - 1; 124 } 125 126 inline int capt() { 127 if(sign) return cap * 3; 128 return cap; 129 } 130 131 };
更小內存消耗的動態數組
注意到上面兩種動態數組常常有些位置是空的,但是有時還是會重新申請新的內存,這對空間是一個極大的浪費(某些卡內存的題目,這個1.5倍可以決定你是AC還是MLE)。首先存數據是連續的,上面已經用了隊列,不難想到用循環隊列。這樣極大地減少了浪費的空間,唯一的缺點就是循環隊列取模很多,會很耗時間,所以還是按需使用吧。下面給出實現。(比較懶,就不寫取模優化了)
1 #include<iostream> 2 #include<algorithm> 3 #include<string.h> 4 #include<malloc.h> 5 using namespace std; 6 typedef bool boolean; 7 8 template<typename T> 9 class Vector { 10 protected: 11 int cap; //容量 12 T* l; //數組部分 13 int front; //頭指針 14 int rear; //尾指針 15 boolean isempty; //動態數組是否是空 16 17 inline void extends(int newsize) { 18 if(!isempty) { 19 T* newarr = (T*)malloc(sizeof(T) * newsize); //開辟新的數組 20 int s = size(); 21 if(rear <= front) { //拷貝數據 22 memcpy(newarr, l + front, sizeof(T) * (cap - front)); 23 memcpy(newarr + (cap - front), l, sizeof(T) * rear); 24 } else { 25 memcpy(newarr, l + front, sizeof(T) * s); 26 } 27 free(l); //釋放原數組 28 l = newarr; 29 front = 0, rear = s; //重新計算數據 30 } else { 31 l = (T*)realloc(l, sizeof(T) * newsize); 32 } 33 cap = newsize; 34 } 35 36 inline void construct() { 37 l = (T*)malloc(sizeof(T) * 4); 38 front = 0, rear = 0, cap = 4, isempty = true; 39 } 40 41 inline int lastPos(int pos) { return (pos + cap - 1) % cap; } 42 inline int nextPos(int pos) { return (pos + 1) % cap; } 43 public: 44 Vector():l(NULL), front(0), rear(0), isempty(true) { } 45 46 /** 47 * 向動態數組尾部追加一個元素。 48 * @param x 追加的元素 49 * @return 如果成功追加,返回true,否則返回false 50 */ 51 inline boolean push_back(T x) { 52 if(l == NULL) { 53 construct(); 54 if(l == NULL) return false; 55 } 56 if(rear == front && !isempty) { 57 extends(cap * 2); 58 if(l == NULL) return false; 59 } 60 l[rear] = x; 61 rear = nextPos(rear); 62 isempty = false; 63 return true; 64 } 65 66 /** 67 * 向動態數組頭部追加一個元素。 68 * @param x 追加的元素 69 * @return 如果成功追加,返回true,否則返回false 70 */ 71 inline boolean push_front(T x) { 72 if(l == NULL) { 73 construct(); 74 if(l == NULL) return false; 75 } 76 if(rear == front && !isempty) { 77 extends(cap * 2); 78 if(l == NULL) return false; 79 } 80 front = lastPos(front); 81 l[front] = x; 82 isempty = false; 83 return true; 84 } 85 86 /** 87 * 在頭部刪除動態數組的一個元素。 88 * @return 如果成功刪除,返回true,否則返回false 89 */ 90 inline boolean pop_front() { 91 if(isempty) return false; 92 front = nextPos(front); 93 if(front == rear) isempty = true; 94 int s = this->size(); 95 int c = this->capt(); 96 if(s < (c >> 2) && c >= 8) { //當當前容量過大時,縮小一下容量 97 extends(cap >> 1); 98 } 99 return true; 100 } 101 102 /** 103 * 在尾部刪除動態數組的一個元素 104 * @return 如果成功刪除,返回true,否則返回false 105 */ 106 inline boolean pop_back() { 107 if(isempty) return false; 108 rear = lastPos(rear); 109 int s = this->size(); 110 int c = this->capt(); 111 if(s < (c >> 2) && c >= 8) { //當當前容量過大時,縮小一下容量 112 extends(cap >> 1); 113 } 114 return true; 115 } 116 117 inline void clear() { 118 free(l); 119 l = NULL; 120 front = 0, rear = 0, cap = 0; 121 isempty = true; 122 } 123 124 inline boolean empty() { 125 return isempty; 126 } 127 128 T& operator [] (int pos) { 129 return l[(front + pos) % cap]; 130 } 131 132 inline int size() { 133 if(rear == front && !isempty) return cap; 134 return (rear - front + cap) % cap; 135 } 136 137 inline int capt() { 138 return cap; 139 } 140 141 };
快速實現隨機下標插入,隨機下標刪除的動態數組
這個嘛。。直接用 splay 好了,反正都是一個 log。。當然也可以自行百度搜一下 stl 中的 deque 的實現。
性能測試
UPD 2018.12.20:重新進行性能測試 (稍微修改了一下上面的warning)
1 #include <bits/stdc++.h> 2 using namespace std; 3 typedef bool boolean; 4 5 namespace Vector_1 { 6 7 template<typename T> 8 class Vector { 9 protected: 10 int cap; 11 int siz; 12 T* l; 13 public: 14 Vector():cap(0), siz(0), l(NULL) { } 15 ~Vector() { 16 delete[] l; 17 l = NULL; 18 } 19 20 inline void push_back(T x) { 21 if(l == NULL) { 22 l = new T[4]; 23 cap = 4, siz = 0; 24 } 25 if(siz == cap) { 26 l = (T*)realloc(l, sizeof(T) * cap * 2); 27 cap = cap << 1; 28 } 29 l[siz++] = x; 30 } 31 32 T& operator [] (int pos) { 33 return l[pos]; 34 } 35 36 inline int size() { 37 return siz; 38 } 39 40 inline int capt() { 41 return cap; 42 } 43 }; 44 45 } 46 47 namespace Vector_2 { 48 49 template<typename T> 50 class Vector { 51 protected: 52 int cap; 53 int siz; 54 boolean sign; 55 T* l; 56 T* fronts; 57 int sizf; 58 59 inline void extends(int newsize) { 60 if(sign) { 61 T* newarr = (T*)malloc(sizeof(T) * newsize); 62 memcpy(newarr, fronts, sizeof(T) * sizf); 63 reverse(newarr, newarr + sizf); 64 memcpy(newarr + sizf, l, sizeof(T) * siz); 65 siz += sizf, cap = newsize; 66 free(l); 67 free(fronts); 68 sizf = 0; 69 fronts = (T*)malloc(sizeof(T) * (newsize >> 1)); 70 l = newarr; 71 } else { 72 l = (T*)realloc(l, sizeof(T) * newsize); 73 cap = newsize; 74 } 75 } 76 public: 77 Vector() : cap(0), siz(0), sign(false), l(NULL), fronts(NULL), sizf(0) { } 78 Vector(boolean sign) : cap(0), siz(0), sign(sign), l(NULL), fronts(NULL), sizf(0) { } 79 ~Vector() { 80 delete[] l; 81 delete[] fronts; 82 } 83 84 inline boolean push_back(T x) { 85 if(l == NULL) { 86 extends(4); 87 if(l == NULL) return false; 88 } 89 if(siz == cap) { 90 extends(cap * 2); 91 if(l == NULL) return false; 92 } 93 l[siz++] = x; 94 return true; 95 } 96 97 inline boolean push_front(T x) { 98 if(!sign) return false; 99 if(fronts == NULL) { 100 extends(4); 101 if(fronts == NULL) return false; 102 } 103 if(sizf == (cap >> 1)) { 104 extends(cap * 2); 105 if(fronts == NULL) return false; 106 } 107 fronts[sizf++] = x; 108 return true; 109 } 110 111 T& operator [] (int pos) { 112 if(pos < sizf) 113 return fronts[sizf - pos - 1]; 114 return l[pos - sizf]; 115 } 116 117 inline int size() { 118 return siz + sizf; 119 } 120 121 inline int capt() { 122 if(sign) return cap + (cap >> 1); 123 return cap; 124 } 125 126 inline int size_front() { 127 return sizf; 128 } 129 130 inline int size_middle() { 131 return siz; 132 } 133 }; 134 135 } 136 137 namespace Vector_3 { 138 139 template<typename T> 140 class Vector { 141 protected: 142 int cap; 143 boolean sign; 144 T* l; 145 int front; 146 int rear; 147 148 inline void extends(int newsize) { 149 if (sign) { 150 int realsize = newsize * 3; 151 T* newarr = (T*)malloc(sizeof(T) * realsize); 152 int s = this->size(); 153 int start_pos = (realsize - s) >> 1; 154 memcpy(newarr + start_pos, l + front + 1, sizeof(T) * s); 155 free(l); 156 l = newarr; 157 front = start_pos - 1, rear = start_pos + s; 158 cap = newsize; 159 } else { 160 l = (T*)realloc(l, sizeof(T) * newsize); 161 cap = newsize; 162 } 163 } 164 165 inline void construct(boolean sign) { 166 if (!sign) { 167 l = (T*)malloc(sizeof(T) * 4); 168 front = -1, rear = 0, cap = 4; 169 } else { 170 l = (T*)malloc(sizeof(T) * 6); 171 front = 1, rear = 2, cap = 2; 172 } 173 } 174 public: 175 Vector() : sign(false), l(NULL), front(-1), rear(0) { } 176 Vector(boolean sign) : sign(sign), l(NULL), front(-1), rear(0) { } 177 ~Vector() { 178 delete[] l; 179 } 180 181 inline boolean push_back(T x) { 182 if (l == NULL) { 183 construct(sign); 184 if(l == NULL) return false; 185 } 186 if (!sign && rear == cap) { 187 extends(cap * 2); 188 if (l == NULL) return false; 189 } else if (sign && rear == cap * 3) { 190 extends(cap * 2); 191 if (l == NULL) return false; 192 } 193 l[rear++] = x; 194 return true; 195 } 196 197 inline boolean push_front(T x) { 198 if (!sign) return false; 199 if (l == NULL) { 200 construct(sign); 201 if(l == NULL) return false; 202 } 203 if (front == -1) { 204 extends(cap * 2); 205 if(l == NULL) return false; 206 } 207 l[front--] = x; 208 return true; 209 } 210 211 inline boolean pop_front() { 212 if (!sign) return false; 213 if (front == rear - 1) return false; 214 front++; 215 int s = this->size(); 216 int c = this->capt(); 217 if (s < (c >> 2) && c >= 12) { 218 extends(cap >> 1); 219 } 220 return true; 221 } 222 223 inline boolean pop_back() { 224 if (front == rear - 1) return false; 225 rear--; 226 int s = this->size(); 227 int c = this->capt(); 228 if (s < (c >> 2) && c >= 12) { 229 extends(cap >> 1); 230 } 231 return true; 232 } 233 234 T& operator [] (int pos) { 235 return l[front + pos + 1]; 236 } 237 238 inline int size() { 239 return rear - front - 1; 240 } 241 242 inline int capt() { 243 if(sign) return cap * 3; 244 return cap; 245 } 246 247 }; 248 249 } 250 251 namespace Vector_4 { 252 253 template<typename T> 254 class Vector { 255 protected: 256 int cap; 257 T* l; 258 int front; 259 int rear; 260 boolean isempty; 261 262 inline void extends(int newsize) { 263 if(!isempty) { 264 T* newarr = (T*)malloc(sizeof(T) * newsize); 265 int s = size(); 266 if(rear <= front) { 267 memcpy(newarr, l + front, sizeof(T) * (cap - front)); 268 memcpy(newarr + (cap - front), l, sizeof(T) * rear); 269 } else { 270 memcpy(newarr, l + front, sizeof(T) * s); 271 } 272 free(l); 273 l = newarr; 274 front = 0, rear = s; 275 } else { 276 l = (T*)realloc(l, sizeof(T) * newsize); 277 } 278 cap = newsize; 279 } 280 281 inline void construct() { 282 l = (T*)malloc(sizeof(T) * 4); 283 front = 0, rear = 0, cap = 4, isempty = true; 284 } 285 286 // inline int lastPos(int pos) { return (pos + cap - 1) % cap; } 287 inline int lastPos(int pos) { 288 return (!pos) ? (cap - 1) : (pos - 1); 289 } 290 inline int nextPos(int pos) { return (pos + 1) % cap; } 291 public: 292 Vector() : l(NULL), front(0), rear(0), isempty(true) { } 293 ~Vector() { 294 delete[] l; 295 } 296 297 inline boolean push_back(T x) { 298 if(l == NULL) { 299 construct(); 300 if(l == NULL) return false; 301 } 302 if(rear == front && !isempty) { 303 extends(cap * 2); 304 if(l == NULL) return false; 305 } 306 l[rear] = x; 307 rear = nextPos(rear); 308 isempty = false; 309 return true; 310 } 311 312 inline boolean push_front(T x) { 313 if(l == NULL) { 314 construct(); 315 if(l == NULL) return false; 316 } 317 if(rear == front && !isempty) { 318 extends(cap * 2); 319 if(l == NULL) return false; 320 } 321 front = lastPos(front); 322 l[front] = x; 323 isempty = false; 324 return true; 325 } 326 327 inline boolean pop_front() { 328 if(isempty) return false; 329 front = nextPos(front); 330 if(front == rear) isempty = true; 331 int s = this->size(); 332 int c = this->capt(); 333 if(s < (c >> 2) && c >= 8) { 334 extends(cap >> 1); 335 } 336 return true; 337 } 338 339 inline boolean pop_back() { 340 if(isempty) return false; 341 rear = lastPos(rear); 342 int s = this->size(); 343 int c = this->capt(); 344 if(s < (c >> 2) && c >= 8) { 345 extends(cap >> 1); 346 } 347 return true; 348 } 349 350 inline void clear() { 351 free(l); 352 l = NULL; 353 front = 0, rear = 0, cap = 0; 354 isempty = true; 355 } 356 357 inline boolean empty() { 358 return isempty; 359 } 360 361 T& operator [] (int pos) { 362 return l[(front + pos) % cap]; 363 } 364 365 inline int size() { 366 if(rear == front && !isempty) return cap; 367 return (rear - front + cap) % cap; 368 } 369 370 inline int capt() { 371 return cap; 372 } 373 374 }; 375 376 } 377 378 const int n = 5e7; 379 380 #define test(msg, func) { \ 381 clock_t begin = clock(); \ 382 func(); \ 383 clock_t end = clock();\ 384 cerr << msg << (end - begin) << '\n'; \ 385 } 386 387 // speed of push_back 388 auto f1_1 = [&] () { 389 Vector_1 :: Vector<int> vec; 390 for (int i = 1; i <= n; i++) 391 vec.push_back(i); 392 }; 393 394 auto f1_2 = [&] () { 395 std :: vector<int> vec; 396 for (int i = 1; i <= n; i++) 397 vec.push_back(i); 398 }; 399 400 auto f1_3 = [&] () { 401 Vector_2 :: Vector<int> vec (false); 402 for (int i = 1; i <= n; i++) 403 vec.push_back(i); 404 }; 405 406 auto f1_4 = [&] () { 407 Vector_2 :: Vector<int> vec (true); 408 for (int i = 1; i <= n; i++) 409 vec.push_back(i); 410 }; 411 412 auto f1_5 = [&] () { 413 Vector_3 :: Vector<int> vec (true); 414 for (int i = 1; i <= n; i++) 415 vec.push_back(i); 416 }; 417 418 auto f1_6 = [&] () { 419 Vector_4 :: Vector<int> vec ; 420 for (int i = 1; i <= n; i++) 421 vec.push_back(i); 422 }; 423 424 // speed of random access & push_back 425 426 typedef class Random { 427 public: 428 unsigned seed; 429 430 Random() : seed(998244353) {} 431 432 unsigned rand() { 433 return seed = seed * seed + seed * 2333 + 3; 434 } 435 }Random; 436 437 auto f2_0 = [] () { 438 static int vec[n]; 439 Random rd; 440 for (int i = 1; i <= n; i++) { 441 vec[rd.rand() % n] = i; 442 } 443 }; 444 445 auto f2_1 = [] () { 446 Vector_1 :: Vector<int> vec; 447 for (int i = 1; i <= n; i++) 448 vec.push_back(i); 449 Random rd; 450 for (int i = 1; i <= n; i++) { 451 vec[rd.rand() % n] = i; 452 } 453 }; 454 455 auto f2_2 = [] () { 456 vector<int> vec; 457 for (int i = 1; i <= n; i++) 458 vec.push_back(i); 459 Random rd; 460 for (int i = 1; i <= n; i++) { 461 vec[rd.rand() % n] = i; 462 } 463 }; 464 465 auto f2_3 = [] () { 466 Vector_2 :: Vector<int> vec (false); 467 for (int i = 1; i <= n; i++) 468 vec.push_back(i); 469 Random rd; 470 for (int i = 1; i <= n; i++) { 471 vec[rd.rand() % n] = i; 472 } 473 }; 474 475 auto f2_4 = [] () { 476 Vector_2 :: Vector<int> vec (true); 477 for (int i = 1; i <= n; i++) 478 vec.push_back(i); 479 Random rd; 480 for (int i = 1; i <= n; i++) { 481 vec[rd.rand() % n] = i; 482 } 483 }; 484 485 auto f2_5 = [] () { 486 Vector_3 :: Vector<int> vec (true); 487 for (int i = 1; i <= n; i++) 488 vec.push_back(i); 489 Random rd; 490 for (int i = 1; i <= n; i++) { 491 vec[rd.rand() % n] = i; 492 } 493 }; 494 495 auto f2_6 = [] () { 496 Vector_4 :: Vector<int> vec; 497 for (int i = 1; i <= n; i++) 498 vec.push_back(i); 499 Random rd; 500 for (int i = 1; i <= n; i++) { 501 vec[rd.rand() % n] = i; 502 } 503 }; 504 505 // push_front & push_back & random access 506 507 auto f3_1 = [] () { 508 Vector_2 :: Vector<int> vec (true); 509 Random rd; 510 for (int i = 1; i <= n; i++) 511 if (rd.rand() & 1) 512 vec.push_back(i); 513 else 514 vec.push_front(i); 515 for (int i = 1; i <= n; i++) { 516 vec[rd.rand() % n] = i; 517 } 518 }; 519 520 auto f3_2 = [] () { 521 Vector_3 :: Vector<int> vec (true); 522 Random rd; 523 for (int i = 1; i <= n; i++) 524 if (rd.rand() & 1) 525 vec.push_back(i); 526 else 527 vec.push_front(i); 528 for (int i = 1; i <= n; i++) { 529 vec[rd.rand() % n] = i; 530 } 531 }; 532 533 auto f3_3 = [] () { 534 Vector_4 :: Vector<int> vec; 535 Random rd; 536 for (int i = 1; i <= n; i++) 537 if (rd.rand() & 1) 538 vec.push_back(i); 539 else 540 vec.push_front(i); 541 for (int i = 1; i <= n; i++) { 542 vec[rd.rand() % n] = i; 543 } 544 }; 545 546 int main() { 547 cerr << "Problem A: \n"; 548 test("Handwritten vector: ", f1_1); 549 test("Handwritten vector II: ", f1_3); 550 test("Handwritten vector II (push_front enable): ", f1_4); 551 test("Handwritten vector III (push_front enable): ", f1_5); 552 test("Handwritten vector IV: ", f1_6); 553 test("std :: vector: ", f1_2); 554 cerr << "Problem B: \n"; 555 test("array: ", f2_0); 556 test("Handwritten vector: ", f2_1); 557 test("Handwritten vector II: ", f2_3); 558 test("Handwritten vector II (push_front enable): ", f2_4); 559 test("Handwritten vector III (push_front enable): ", f2_5); 560 test("Handwritten vector IV: ", f2_6); 561 test("std :: vector: ", f2_2); 562 cerr << "Problem C: \n"; 563 test("Handwritten vector II:", f3_1); 564 test("Handwritten vector III:", f3_2); 565 test("Handwritten vector IV:", f3_3); 566 return 0; 567 }
Result
Problem A : $5\times 10^7$次push_back
Problem B : $5\times 10^7$次push_back & 隨機下標訪問 (請手動和 push_back 的時間作差來計算訪問時間)
Problem C : $5\times 10^7$次push_front / push_back & 隨機下標訪問

開啟O2:

最后呢,歡迎提出問題或指出上面的錯誤。
