记账法
我们对不同的操作赋予不同的花销,任意一个操作的花销比他们的实际花销或多或少,我们对一个操作赋予的花销叫做平摊花销。当某个操作的平摊花销超过了它的实际花销,多出的部分就作为“余额”存入某些特定元素的账户里;当某个操作的实际花销大于平摊花销时,就用之前的余额来支付差额。
命题
假设S是一个用动态数组实现的序列,当序列中元素个数等于序列的容量时,将序列的容量扩充为原来的2倍。则对S(初始容量为1)进行n次添加元素的操作所需要的时间为O(n)
证明
- 假设除了需要扩充序列容量之外的一次添加元素的操作,需要花费1美元,把序列的容量从k扩充为2k需要花费k美元
- 我们给每个添加元素操作设定3美元的平摊花销,多出的2美元存在该元素的账户里
- 当S中元素的数量为2i时,需要将S的容量扩充为2i+1,所需要的花销为2i美元
- 上一次使用余额是S中元素数量为2i-1时,只使用了2i-1之前的余额,所以2i-1到2i-1的元素账户里的余额是没有使用过的,共有 2 * (2i - 1 - 2i-1 + 1) = 2i个,恰好等于本次S扩充容量所需要的花销
- 因此,添加n个元素所需的时间为3n,即O(n)
示例
如上图,当序列中元素个数为8时,再添加元素需要先把序列容量扩充到16,共需要8美元。由于序列上一次扩充容量是从4到8,所以下标为4-7的元素账户中的余额是没有被使用的,恰好有8个,可以用来支付本次扩充容量所需的花销。然后再把新元素添加到下标为8的槽(slot)里,实际花销是1美元,平摊花销是3美元,多出的2美元存入它的账户里用于支付下次扩充序列容量。