内存为 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_2n log2n次扩容,每次需要复制 2 i 2^i 2i个元素,则一共需要复制元素的次数为
∑ i = 1 l o g 2 n 2 i = 2 ( n − 1 ) \sum\limits_{i=1}^{log_2n}2^i=2(n-1) i=1∑log2n2i=2(n−1)
则平均每次插入需要执行 2 ( n − 1 ) n \frac{2(n-1)}{n} n2(n−1)次复制,
即平均每次执行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=1∑2n2i=(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+⋯+mn−1=m−1mn−1
新分配的内存:
m n m^n mn
2倍扩容 ( m = 2 ) (m=2) (m=2):
2 n − 1 < 2 n 2^n-1<2^n 2n−1<2n
前面使用过的内存不够用于新内存的分配。
1.5倍扩容 ( m = 1.5 ) (m=1.5) (m=1.5):
2 ( 1. 5 n − 1 ) > 1. 5 n 2(1.5^n-1)>1.5^n 2(1.5n−1)>1.5n
解得
n > l o g 1.5 2 n>log_{1.5}2 n>log1.52
即第 l o g 1.5 2 log_{1.5}2 log1.52次之后扩容,可以用前面使用过的内容用于新内存的分配。