24.vector的增加删除都是怎么做的?为什么是1.5或者是2倍?

发布时间 2023-08-03 07:32:06作者: CodeMagicianT

24.vector的增加删除都是怎么做的?为什么是1.5或者是2倍?

size()函数返回的是已用空间大小,capacity()返回的是总空间大小,capacity()-size()则是剩余的可用空间大小。当size()和capacity()相等,说明vector目前的空间已被用完,如果再添加新元素,则会引起vector空间的动态增长。

由于动态增长会引起重新分配内存空间、拷贝原空间、释放原空间,这些过程会降低程序效率。因此,可以使用reserve(n)预先分配一块较大的指定大小的内存空间,这样当指定大小的内存空间未使用完时,是不会重新分配内存空间的,这样便提升了效率。只有当n>capacity()时,调用reserve(n)才会改变vector容量。

resize()成员函数改变元素的数目,至于空间的的变化需要看具体情况去分析,如下:

void resize(size_type __new_size, const _Tp& __x) 
{
    if (__new_size < size())
        erase(begin() + __new_size, end());
    else
        insert(end(), __new_size - size(), __x);
}

1.空的vector对象,size()和capacity()都为0

2.当空间大小不足时,新分配的空间大小为原空间大小的2倍。

3.使用reserve()预先分配一块内存后,在空间未满的情况下,不会引起重新分配,从而提升了效率。

4.当reserve()分配的空间比原空间小时,是不会引起重新分配的。

5.resize()函数只改变容器的元素数目,未改变容器大小。

6.用reserve(size_type)只是扩大capacity值,这些内存空间可能还是“野”的,如果此时使用“[ ]”来访问,则可能会越界。而resize(size_type new_size)会真正使容器具有new_size个对象。

整体的一个扩容流程为:申请新的内存空间(空间大小为原空间的两倍或一点五倍)—> 把原空间的元素拷贝到新的空间里 —> 释放原空间 —> 数组指针指向新空间。

不同的编译器,vector有不同的扩容大小。在vs下是1.5倍,在GCC下是2倍;

空间和时间的权衡。简单来说, 空间分配的多,平摊时间复杂度低,但浪费空间也多。

使用k=2增长因子的问题在于,每次扩展的新尺寸必然刚好大于之前分配的总和,也就是说,之前分配的内存空间不可能被使用。这样对内存不友好,最好把增长因子设为(1, 2],也就是1-2之间的某个数值。

对比可以发现采用采用成倍方式扩容,可以保证常数的时间复杂度,而增加指定大小的容量只能达到O(n)的时间复杂度,因此,使用成倍的方式扩容。

1.第一个问题 :
如果以成倍方式增长
假定有 n 个元素,倍增因子为 m; 完成这 n 个元素往一个 vector 中的 push_back操作,需要重新分配内存的次数大约为logmn,就是说如果这n个元素都需要扩容才可以加入,那么最坏的情况下也是需要分配内存的次数是logmn;
第 i 次重新分配将会导致复制 mi (也就是当前的vector.size() 大小)个旧空间中元素;n 次 push_back 操作所花费的时间复制度为O(n),如下图所示:

m / (m - 1),这是一个常量,均摊分析的方法可知,vector 中 push_back 操作的时间复杂度为常量时间.

但是如果一次增加固定值大小
假定有 n 个元素,每次增加k个;第i次增加复制的数量为为:100i ;n 次 push_back 操作所花费的时间复杂度为O(n2):

均摊下来每次push_back 操作的时间复杂度为O(n);

总结对比
可以发现采用采用成倍方式扩容,可以保证常数的时间复杂度,而增加指定大小的容量只能达到O(n)的时间复杂度,因此,使用成倍的方式扩容。