浅谈动态数组原理及其实现

  stl中的vector是竞赛中常用的容器,原因在于省内存,$O(1)$ 在后端插入和删除、随机下标访问,今天就来谈谈它的实现。

最简单的一个动态数组

   动态数组并不是真正意义上的动态的内存,而是一块连续的内存,当添加新的元素时,容量已经等于当前的大小的时候(存不下了),执行下面3步

  1. 重新开辟一块大小为当前容量两倍的数组
  2. 把原数据拷贝过去
  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 }
Code

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:

最后呢,欢迎提出问题或指出上面的错误。

posted @ 2017-06-25 12:15  阿波罗2003  阅读(8921)  评论(0编辑  收藏  举报