zoukankan      html  css  js  c++  java
  • 浅谈动态数组原理及其实现

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

    最简单的一个动态数组

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

    1. 重新开辟一块大小为当前容量两倍的数组
    2. 把原数据拷贝过去
    3. 释放掉旧的数组

      完事后再把这个元素加在后面。

      那么你一定会很好奇,它为什么会是 $O(1)$。这个是均摊下来的结果,我们只需要证明总共拷贝的元素个数是O(n)级别的就好了(因为释放内存和申请内存都比较快,详情自行百度吧)。

      总共拷贝的元素个数是 $1 + 2 + 4 + cdots+ 2^{lfloor log_2 n floor}$ ,根据等比数列求和公式那么有这个和等于 $2^{lfloor log_2 n floor + 1} - 1$ 。因为 $2^{lfloor log_2 n floor + 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) << '
    '; 
    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: 
    ";
    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: 
    ";
    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: 
    ";
    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 imes 10^7$次push_back

    Problem B : $5 imes 10^7$次push_back & 随机下标访问 (请手动和 push_back 的时间作差来计算访问时间)

    Problem C : $5 imes 10^7$次push_front / push_back & 随机下标访问

    开启O2:

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

  • 相关阅读:
    小程序组件化、自定义组件 、自定义组件练习,组件与页面互相传值,插槽(slot)、 component构造器
    小程序事件的介绍、事件对象、事件传值、事件冒泡和捕获,bind和catch的区别
    小程序列表渲染、模板语法、wxml的引入、wxs
    小程序中的常用组件,及组件的共同属性、wxcss、weui-wxss、Mustache语法、逻辑判断wx:if、wx:elif、wx:else
    小程序启动时加载顺序,小程序初始化,页面初始化
    label和input标签在兼容模式下 不能关联?解决方案
    当一个cdn挂掉之后,让加载另一个cdn
    将summernote在vue项目中封装成组件
    wangEditor3富文本编辑器的使用
    git中 连接远程仓库及tag切换的使用
  • 原文地址:https://www.cnblogs.com/yyf0309/p/vector.html
Copyright © 2011-2022 走看看