• STL vector动态扩容


    1. 基本操作

    #include<iostream>
    #include<vector>
    using namespace std;
    
    int main()
    {
        vector<int>v1;
        vector<int>v2(4);
        vector<int>v3(1,4);
    
        v1.push_back(1);
        v1.push_back(2);
        v1.push_back(3);
        v1.push_back(4);
        v1.push_back(5);
    
        // 这里清除区间,左闭右开
        v1.erase(v1.begin()+2, v1.end()-1);
    
        v2 = v1;
        if(!v2.empty())
        {
            for(const auto i : v2)
            {
                cout << i << " ";
            }
            cout << endl;
            cout << "v1 size: " << v2.size() << endl;
            v2.~vector();
        }
        return 0;
    }
    
    
    1. push_back和pop_back操作
    都只是对尾部进行操作,
    push_back:从尾部插入数据时,当数组还有备用空间时就直接插入尾部就行,如果没有就重新寻找更大的空间并将数据赋值过去
    void push_back(const T& x)
    {
          // 如果还没有到填满整个数组, 就在数据尾部插入
          if (finish != end_of_storage)
          {
                construct(finish, x);
                ++finish;
          }
        // 数组被填充满, 调用insert_aux必须重新寻找新的更大的连续空间, 再进行插入
          else
              insert_aux(end(), x);
    }
    
    pop_back: 从尾部删除,使用空间的尾自减并调用析构函数,但是并没有释放内存
    
    void pop_back()
    {
         --finish;
         destroy(finish);
    }
    
    finish始终都指向最后一个元素的后一个位置的地址.
    
    
    2. 容器大小的调整
    reverse: 修改容器的大小  空间大小
    void reverse(size_type n)
    {
        // 修改容器的大小要大于之前的容器大小
        
    }
    
    resize: size()  使用大小, 变小释放,变大用尾部填充
    
    new_size大于cap,小于2*cap,则用cap = 2*new_size
            大于2*cap,则cap = new_size
    
    void resize(size_type new_size, const T& x)
    {
        // 元素大小大于了要修改的大小, 则释放掉超过的元素
          if (new_size < size())
            erase(begin() + new_size, end());
        // 元素不够, 就从end开始到要求的大小为止都初始化x
          else
            insert(end(), new_size - size(), x);
    }

    2. reverse 和 resize

    // vector::reserve
    #include <iostream>
    #include <vector>
    
    int main ()
    {
      std::vector<int>::size_type sz;
    
      std::vector<int> foo;
      sz = foo.capacity();
      std::cout << "making foo grow:
    ";
      for (int i=0; i<1000; ++i) {
        foo.push_back(i);
        //std::cout << "i: " << i << '
    ';
        if (sz!=foo.capacity()) {
          sz = foo.capacity();
          std::cout << "capacity changed: " << sz << '
    ';
        }
      }
    
      std::cout << "foo size: " << foo.size() << '
    ';
      std::cout << "cap: " << foo.capacity() <<'
    ';
    
      sz = foo.capacity();
      for(int i = 0;i < 1000;i++)
      {
          //std::cout << foo.pop_back() << '
    ';
          std::cout << "i: " << i << " cap: " << foo.capacity() << " size: " << foo.size()<< '
    ';
          foo.pop_back();
          if (sz!=foo.capacity()) {
            sz = foo.capacity();
            std::cout << "capacity changed: " << sz << '
    ';
        }
      }
    
      return 0;
    }

    3. 测试

    #include <iostream>
    #include <vector>
    using namespace std;
    
    int main ()
    {
        std::vector<int>::size_type sz;
        std::vector<int> foo;
        vector<int>v1;
        for(int i = 0;i < 12;i++)
        {
            v1.push_back(i);
            cout << v1.capacity() << " " << v1.size() << endl;
        }
        cout << "max_size: " << v1.max_size() << endl;
    }

    可见,VS中按1.5倍扩容,GCC以2倍扩容。

    一种不调用析构函数将vector清空的方法:

    vector<int>().swap(v1);

    4. 扩容因子

    实际上,C++标准并没有push_back要用哪个增长因子,这是由标准库的实现者决定的。

    如何选取扩容因子呢?

    从空间角度:扩容因子越大,预留的空间就越大,浪费的空间也越多

    从时间角度:扩展到相同长度下,K越小,扩容的次数越多,时间开销越大

    假设扩容因子为k,扩容后的最终长度为n,这意味着需要扩容 $log_kn$ 次,

    这元素复制和开辟内存的时间正比于: $t = 1 + k + k^2 + ... + k^{log_kn -1} = frac{n-1}{k-1}$,可见k越小越好。

    如何达到时间和空间的平衡呢?

    我们来看一下K = 2时的情况。

    每次扩容后capacity的情况如下:1,2,4,8,16,32 ……..

    当我们释放了4的空间,我们寻找8的新空间,再次扩容,释放8,寻找16。。

    仔细分析,第5次扩容时,需要寻找16的新空间,第4次释放了8,第3次释放了4,第2次释放了2,第1次释放了1,所以 1 + 2 + 4 + 8 = 15 < 16,也就意味着,之前释放的空间,永远无法被下一次的扩容利用,这对内存与cache是非常不友好的。

    我们再来看一下K = 1.5的情况。

    每次扩容之后capacity的情况为:1,2,3,4,6,9,13,19,28 ……

    再按刚才的思路分析一遍,1 + 2 >= 3; 2 + 3 + 4 >= 6; 6 + 9 >= 13 …….

    所以,当K为1.5时,显然对内存和cache要友好很多,至少从容量上来说,是存在重复利用的可能性的。

    因此,我们可以得出结论,当K = 2时,时间上要比 K = 1.5 占优,而空间上比 1.5 稍有劣势。

    理论最优扩容因子是多少呢?

    继续刚才的分析,我们希望的是,上几次的空间,存在被下一次扩容时利用的可能性。

    也就是 X(n-2) + X(n-1) >= X(n),显然我们也希望时间上也要更好,即X(n-2) + X(n-1) = X(n)

    即:1,2,3,5,8,13,21,34,55 。。。。

    是不是很熟悉。。。是的,这就是我们的斐波那契数列。。。

    那么当N趋于无限大时,取极限,最佳的扩容因子也就是那个最美的数,黄金分割率,1.618。

    参考链接:

    1. CSDN_denghe1122-STL vector (一)——扩容原理与内存分配与释放

    2. cplusplus-vector

    3. CSDN_gettogetto-当面试官问我们vector扩容机制时,他想问什么?

  • 相关阅读:
    深入理解Java虚拟机-走进Java
    springboot服务引入外部jar包在windows运行正常,在linux环境上无法加载到引入jar包的类
    ActiveMQ数据接收类型问题
    kafka报文一直打印的问题
    Java基本语法
    flask跨域问题
    flask接口传参
    iTextSharp导出PDF模板(报告)
    ASP.NET中<%=%>、<%%>、<%@%>、<%#%>的用法与区别
    python AES+SHA1PRNG
  • 原文地址:https://www.cnblogs.com/lfri/p/12410966.html
Copyright © 2020-2023  润新知