vector是如何增长的:
为了支持快速随机访问,vector是连续存储的。
当添加一个新元素时,如果没有空间容纳新元素,为了保持连续存储,容器必须分配新的内存空间保存已有元素和新元素。
转移流程:申请新空间,转移元素,释放旧空间。
为了避免每次添加元素都需要转移一次空间,当需要扩容时,vector会申请一个比需求更大的内存空间,即预留一部分空间作为备用。
push_back复杂度分析:
push_back均摊后的时间复杂度为O(1)。
vector只有当达到capacity之后才会扩容。
设倍增因子为m,即达到容量上限后,新空间容量翻m倍。
现在假设要往vector丢入n个元素,
假设要扩容k次,那么有:
1
+
m
+
m
2
+
m
3
.
.
.
+
m
k
>
=
n
1+m+m^2+m^3...+m^k>=n
1+m+m2+m3...+mk>=n
根据等比数列前n项和公式,式子可以化简为:
m
k
−
1
>
=
n
∗
(
m
−
1
)
m^k-1>=n*(m-1)
mk−1>=n∗(m−1)
k
=
l
o
g
m
n
∗
(
m
−
1
)
=
l
o
g
m
n
+
l
o
g
m
(
m
−
1
)
≈
l
o
g
m
n
k=log_{m}{n*(m-1)}=log_{m}n+log_{m}(m-1) \approx log_{m}n
k=logmn∗(m−1)=logmn+logm(m−1)≈logmn
即将会进行
l
o
g
m
n
次
扩
容
操
作
log_{m}n次扩容操作
logmn次扩容操作
k次扩容过程中,复制数据需要的操作次数大概为:
∑
i
=
1
k
m
i
=
m
∗
(
m
k
−
1
)
m
−
1
=
m
∗
(
m
l
o
g
m
n
−
1
)
m
−
1
=
m
∗
(
n
−
1
)
m
−
1
\sum_{i=1}^{k} m^i=\frac{m*(m^k-1)}{m-1}=\frac{m*(m^{log_{m}n}-1)}{m-1}=\frac{m*(n-1)}{m-1}
∑i=1kmi=m−1m∗(mk−1)=m−1m∗(mlogmn−1)=m−1m∗(n−1)
均摊给n次push_back,那么:
m
∗
(
n
−
1
)
m
−
1
/
n
<
m
m
−
1
\frac{m*(n-1)}{m-1}/n<\frac{m}{m-1}
m−1m∗(n−1)/n<m−1m
m m − 1 \frac{m}{m-1} m−1m是常数级别的。
复制部分均摊O(1),单个元素新增O(1),因此均摊后的总复杂度为O(1)。