作者:叶小哈
链接:https://www.nowcoder.com/discuss/37140
来源:牛客网
【常规】
push_back的话,一般来说,都是按两倍来扩容,因为push_back每次都是只插入一个数据
insert的话,因为可以一次插入多个数据,所以要复杂一些。
【进阶】
其实,那个两倍也不是一定的,C++标准并没有规定vector到底应该按多少来扩容,有一些选择2,有一些选择1.5。那么一般是根据什么来选择这个扩容因子的呢?
确定扩容因子的大小,主要从时间和空间的角度来分析。
从空间上来分析,扩容因子越大,意味着预留空间越大,浪费的空间也越多,所以从空间考虑,扩容因子因越小越好。
从时间上来分析,如果预留空间不足的话,就需要重新开辟一段空间,把原有的数据复制到新空间,如果扩容因子无限大的话,那显然就不再需要额外开辟空间了。所以时间角度看,扩容因子越大越好。
还可以从摊还分析的角度来分析,为了简化分析,就不考虑一次性插入大量数据的情况,只考虑push_back的情况:
假设扩容因子为K,vector最后的长度为N
显然每次常规插入操作需要1的时间,插入n个数据的常规时间为N
现在考虑额外的消耗,扩容因子为K,意味着一共需要扩容logkN次(以K为底,N的对数),为简化计算,假设是容量是从1开始,也就是要对下面的等比序列求和
S = 1 + k + k^2 + k^3 + k^4 + …… + k^(logkN) = (N-1)/(K-1)
显然,也就意味着扩容因子K越大,额外的消耗越小。总消耗为N+(N-1)/(K-1) 。当K大于1时,K越大总消耗越小。
[BYTHEWAY]
顺便可以再求一下push_back的摊还时间复杂度。即为(N+(N-1)/(K-1))/N = (NK-1)/(K-1)
【再进阶】
既然从时间上考虑K越大越好,从空间考虑K越小越好,那么有没有一个数为最佳,同时协调好空间与时间呢?
我们从内存上来考虑,vector数据实际存放在堆上,vector的每次扩容需要把旧的释放,再重新开辟一段新的连续内存。
我们来看一下K = 2时的情况。
每次扩容后capacity的情况如下:1,2,4,8,16,32 ……..
当我们释放了4的空间,我们寻找8的新空间,再次扩容,释放8,寻找16。。
仔细分析,第5次扩容时,需要寻找16的新空间,第4次释放了8,第3次释放了4,第2次释放了2,第1次释放了1,所以 1 + 2 + 4 + 8 = 15 < 16,也就意味着,之前释放的空间,永远无法被下一次的扩容利用,这对内存与cache是非常不友好的。
我们再来看一下K = 1.5的情况。
每次扩容之后capacity的情况为:1,2,3,4,6,9,13,19,28 ……
再按刚才的思路分析一遍,1 + 2 >= 3; 2 + 3 + 4 >= 6; 6 + 9 >= 13 …….
所以,当K为1.5时,显然对内存和cache要友好很多,至少从容量上来说,是存在重复利用的可能性的。
因此,我们可以得出结论,当K = 2时,时间上要比 K = 1.5 占优,而空间上比 1.5 稍有劣势。
那么,那个最佳的数是多少呢?
继续刚才的分析,我们希望的是,上几次的空间,存在被下一次扩容时利用的可能性。
也就是 X(n-2) + X(n-1) >= X(n),显然我们也希望时间上也要更好,即X(n-2) + X(n-1) = X(n)
即:1,2,3,5,8,13,21,34,55 。。。。
是不是很熟悉。。。是的,这就是我们的斐波那契数列。。。
那么当N趋于无限大时,取极限,最佳的扩容因子也就是那个最美的数,黄金分割率,1.618。
https://blog.csdn.net/yangshiziping/article/details/52550291