C++之vector扩容分析

内存为 v e c t o r vector vector容器分配了一块连续的空间,每一个元素紧挨着前一个元素存放, s i z e ( ) size() size()表示 v e c t o r vector vector容器中存放的元素个数, c a p a c i t y ( ) capacity() capacity()表示内存为 v e c t o r vector vector容器分配的空间所能存放的元素个数,当 v e c t o r vector vector容器中存放的元素个数达到了 c a p a c i t y ( ) capacity() capacity()规模,则需要对 v e c t o r vector vector容器进行扩容操作,即内存重新分配一块更大的空间,将 v e c t o r vector vector原先存放的元素拷贝到新分配的空间。
不同的编译器所实现的扩容方式也不同,常见的有1.5倍扩容和2倍扩容。
假设当容器中元素的个数为n时,需要对其进行扩容处理,每次扩容成倍地增加内存和增加一个固定大小的内存,其时间复杂度都为 O ( n ) O(n) O(n),但是为什么要选择前者而非后者,这涉及到分摊时间复杂度的概念。
下面以两倍扩容和固定大小取2为例,假设我们执行了n次push_back操作,如下图所示:

第n次: n n
第9次: 9 9
第9次: 8 8
第7次: 7 7
第6次: 6 6
第5次: 5 5
第4次: 4 4
第3次: 3 3
第2次: 2 2
第1次: 1 1

红色部分为元素个数达到capacity(),再执行push_back需要进行扩容操作。

左边一列为两倍扩容执行push_back操作:

当插入n个元素时,大概需要进行 l o g 2 ⁡ n log_2⁡n log2n次扩容,每次需要复制 2 i 2^i 2i个元素,则一共需要复制元素的次数为
∑ i = 1 l o g 2 ⁡ n 2 i = 2 ( n − 1 ) \sum\limits_{i=1}^{log_2⁡n}2^i=2(n-1) i=1log2n2i=2(n1)
则平均每次插入需要执行 2 ( n − 1 ) n \frac{2(n-1)}{n} n2(n1)次复制,
即平均每次执行push_back的时间复杂度为 O ( 1 ) O(1) O(1)

右边一列为每次固定增长大小为2的扩容执行push_back操作:

当插入n个元素时,大概需要进行 n / 2 n/2 n/2次扩容,每次需要复制 2 i 2i 2i个元素,则一共需要复制的元素个数为
∑ i = 1 n 2 2 i = ( 1 + n 2 ) n \sum\limits_{i=1}^{\frac{n}{2}}2^i=(1+\frac{n}{2})n i=12n2i=(1+2n)n
则平均每次插入需要执行 1 + n 2 1+\frac{n}{2} 1+2n次复制,
即平均每次执行push_back的时间复杂度为 O ( n ) O(n) O(n)

相比于2倍扩容,1.5倍扩容在分配新内存时能够利用前面使用过的内存

前面使用过的内存:
1 + m + m 2 + m 3 + ⋯ + m n − 1 = m n − 1 m − 1 1+m+m^2+m^3+⋯+m^{n-1}=\frac{m^n-1}{m-1} 1+m+m2+m3++mn1=m1mn1
新分配的内存:
m n m^n mn
2倍扩容 ( m = 2 ) (m=2) (m=2):
2 n − 1 &lt; 2 n 2^n-1&lt;2^n 2n1<2n
前面使用过的内存不够用于新内存的分配。
1.5倍扩容 ( m = 1.5 ) (m=1.5) (m=1.5)
2 ( 1. 5 n − 1 ) &gt; 1. 5 n 2(1.5^n-1)&gt;1.5^n 2(1.5n1)>1.5n
解得
n &gt; l o g 1.5 ⁡ 2 n&gt;log_{1.5}⁡2 n>log1.52
即第 l o g 1.5 ⁡ 2 log_{1.5}⁡2 log1.52次之后扩容,可以用前面使用过的内容用于新内存的分配。

猜你喜欢

转载自blog.csdn.net/Huang_JinXin/article/details/100176794