可扩充向量
看完你能讲清可扩充向量如何搬家扩容,以及代价怎么摊平
按 空格/→ 演示下一步
全部页面点击任意一页,跳回舞台从这页播放
可扩充向量
看完你能讲清可扩充向量如何搬家扩容,以及代价怎么摊平
静态空间的困境
上一页我们说向量要在内存里连续存放。可问题来了:建向量时得先向系统要一块地,要多大呢?要小了数据装不下,要大了平时又白白空着——这个两难,就是本页的主角。
车位建好就定死,车多了进不来,车少了空着
动态空间管理
上页看到静态空间一满就束手无策。生活中房子住不下怎么办?换一套更大的,把家具搬过去,再退掉旧房。向量遇到容量不足时,用的正是这套「换房」策略。
换大房对应申请新空间,搬家具对应复制元素,退租对应释放旧空间
扩容的完整流程
空间不够时,向量按四步完成一次扩容,每步各司其职。
递增式扩容
上一页我们看到扩容流程已打通,但还有一个关键决策没定:每次扩多少?搬家太频繁会累,一次搬太大又浪费——增量策略就是在这两者间找平衡。
每次固定加半杯水对应每次追加固定增量,够用再续
加倍式扩容
上一页的递增式扩容每次只多要一点,扩得慢。想想搬家:与其一趟趟搬一个箱子,不如直接换套大一倍的房子——容量翻倍,次数骤减,这就是加倍式扩容。
房子翻倍对应容量翻倍,搬家次数对应扩容次数
递增 vs 加倍
两者都能自动扩容,但扩多少差一个数量级——这直接决定扩容次数与摊还代价。
- 每次固定加常数 c,如 +10
- n 次插入约扩容 n/c 次
- 总搬移代价 O(n²)
- 每次容量翻倍,如 ×2
- n 次插入约扩容 log n 次
- 总搬移代价 O(n)
扩容代码实现
一段真实的 C++ expand 实现,看扩容的判断、加倍与搬迁如何落地。
先判断是否真满(多余调用零开销),容量按加倍翻番,再逐元素复制到新空间,最后释放旧区——正是加倍式扩容流程的代码化。
为什么不能只看最坏情况
上一页我们看到加倍式扩容看起来最划算。但如果按单次最坏情况算,一次扩容要搬动全部 n 个元素,成本高得吓人——问题出在衡量方式上。
单次入场最坏要花全款,但摊到每次入场后近乎免费
分摊复杂度
上一页我们看到:只盯单次扩容的最坏代价,会得出「动态数组很慢」的错误结论。更公平的算法是把罕见的大开销摊到之前那些廉价操作上,按平均代价来评估。
一次大额总账平摊到每个人,单人均价就变小了
两种策略的分摊分析
从 n 次插入出发,沿两条分支看每次扩容搬多少元素,最后对比总代价与均摊复杂度。
自测:选对策略
向量采用加倍式扩容,连续追加 n 个元素,为何总耗时是 O(n) 而不是 O(n²)?
要点收束
- ✓静态容量不足时需动态扩容,容量按需增长
- ✓递增式省内存但摊还 O(√n),加倍式 O(1)
- ✓扩容虽有开销,摊还后单次操作均摊代价很低
- ✓摊还分析看长期平均,不被单次最坏迷惑
- ✓选策略先问:内存敏感还是操作吞吐优先
课后思考
先独立想几分钟,再对照参考答案——开放问题没有唯一解。
参考答案扩容瞬间确实要整体搬迁,是 Θ(n) 的昂贵操作。分摊分析把这次开销摊到之前 n 次廉价插入上,平均每次仍是常数——不等于单次变快,而是长期总账变好。
参考答案可先用 reserve 预分配接近上限的容量,避免反复搬迁;必须扩容时也可选较小的增长因子,用稍高的分摊代价换取更少的空闲空间——按场景权衡。
参考答案全部失效——它们指向旧内存,扩容后继续使用是未定义行为。这也是许多语言文档强调'扩容后迭代器失效'的原因,持有引用前要想清楚生命周期。