内存为
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次之后扩容,可以用前面使用过的内容用于新内存的分配。