动态表

概述

动态表(Dynamic Table)是一种能够自动调整大小的数组实现,支持高效的插入和删除操作。当表满时进行扩张(通常倍增),当表过于稀疏时进行缩容。通过摊还分析可以证明,在合理的扩张/缩容策略下,每次操作的摊还代价为

定义

动态表

动态表 是一个支持动态调整大小的数组,维护以下状态:

  • :当前存储的元素个数
  • :当前分配的总槽位数
  • 负载因子(load factor)

支持两种基本操作:

  • TABLE-INSERT(T, x):插入元素
  • TABLE-DELETE(T, x):删除元素

核心性质

  1. 负载因子 是衡量表利用率的指标。理想情况下 接近 ,但需要为插入留出空间。
  2. 几何扩张策略:当表满()时,分配一个更大的表(通常为原来的 倍),将所有元素复制到新表中。单次扩张代价为 ,但摊还代价为
  3. 防抖动设计:缩容阈值设为 (而非 ),避免在插入和删除交替进行时出现反复扩张和缩容的”抖动”(thrashing)现象。这确保了连续扩张和缩容之间有足够的”缓冲区”。
  4. 摊还 保证:通过合理的势能函数设计,可以证明在仅插入或插入+删除场景下,每次操作的摊还代价均为

表扩张机制

当执行 TABLE-INSERT 且当前表已满时:

  1. 分配新表,大小为
  2. 将旧表中所有 个元素复制到新表
  3. 插入新元素
  4. 释放旧表

代价分析:扩张操作的代价为 (复制所有元素),但通过摊还分析,每次插入的摊还代价仅为

表缩容机制

当执行 TABLE-DELETE 且负载因子降至 时:

  1. 分配新表,大小为 (但至少为
  2. 将剩余元素复制到新表
  3. 释放旧表

防抖动原理:缩容阈值为 而非 。假设刚缩容后 (因为 ,缩半后 )。要触发下一次扩张需要将表填满(),即需要再插入 个元素;要触发下一次缩容需要删除到 ,即需要再删除 个元素。这保证了在两次调整之间有 次操作。

势能函数设计

仅插入场景

定义势能函数:

验证

  • 初始状态:
  • 非负性: 时(刚扩张后)

分析

  • 无扩张时:
  • 有扩张时:

每次插入摊还代价为

插入+删除场景(分段势能函数)

定义分段势能函数:

该函数在 处连续(值为 ),且始终非负。可以证明插入和删除的摊还代价均为

实际编程语言实现

语言/数据结构初始容量扩张因子缩容策略
Python list0(动态分配)(约 按比例缩容
Java ArrayList10缩至当前
C++ std::vector0C++11 起 shrink_to_fit
Go slice0无自动缩容

实现差异

Python 使用 而非 的扩张因子,牺牲了摊还常数因子以换取更小的内存开销。C++ 的 扩张在理论上给出最优的摊还常数,但实际内存利用率可能较低。

参见