聚合分析 (aggregate analysis)
一个 n 个操作的序列最坏情况下花费的总时间为 T(n)T(n), 则在最坏情况下, 每个操作的摊还代价为 T(n)nT(n)n
如栈中的 push, pop 操作都是 O(1)O(1), 增加一个新操作 multipop
,
def multipop(stk,k):
while not stk.empty() and k>0:
stk.pop()
k-=1
multipop 的时间复杂度为 min(stk.size,k), 最坏情况为 O(n)O(n), 则 n 个包含 push pop multipop 的操作列的最坏情况是 O(n2)O(n2), 并不是这样, 注意到, 必须栈中有元素, 再 pop, 所以 push 操作与 pop 操作 (包含 multipop 中的 pop), 个数相当, 所以 实际上应为 O(n)O(n), 每个操作的摊还代价 为 O(1)O(1)
核算法 (accounting method)
对不同操作赋予不同费用 cost (称为摊还代价 c′ici′), 可能多于或者少于其实际代价 cici
当 c′i>cici′>ci, 将 c′i−cici′−ci( credit
) 存入数据结构中的特定对象.. 对于后续 c′i<cici′<ci 时, 可以使用这些 credit 来 支付差额.. 有要求
如栈
op | c′ici′ | cici |
---|---|---|
push | 2 | 1 |
pop | 0 | 1 |
multipop | 0 | min(s,k) |
由核算法, 摊还代价满足要求, 所以 n 个操作总代价 O(n)O(n), 每个操作摊还代价为 O(1)O(1)
势能法 (potential method)
势能释放用来支付未来操作的代价, 势能是整个数据结构的, 不是特定对象的 (核算法是).
数据结构 D0D0 为初始状态, 依次 执行 n 个操作 opiopi 进行势能转换 Di=opi(Di−1),i=1,2,…,nDi=opi(Di−1),i=1,2,…,n , 各操作代价为 cici
势函数 Φ:Di→RΦ:Di→R, Φ(Di)Φ(Di) 即为 DiDi 的势
则第 i 个操作的摊还代价
则
如果定义一个势函数 Φ,st Φ(Di)⩾Φ(D0)Φ,st Φ(Di)⩾Φ(D0), 则总摊还代价给出了实际代价的一个上界
可以简单地以 D0为参考状态,then Φ(D0)=0D0 为参考状态,then Φ(D0)=0
例如栈操作,
设空栈为 D0D0, 势函数定义为栈的元素数
对于 push, Φ(Di)−Φ(D0)=1Φ(Di)−Φ(D0)=1
则 c′=c+Φ(Di)−Φ(D0)=c+1=2c′=c+Φ(Di)−Φ(D0)=c+1=2
对于 multipop, Φ(Di)−Φ(D0)=−min(k,s)Φ(Di)−Φ(D0)=−min(k,s)
则 c′=c−min(k,s)=0c′=c−min(k,s)=0
同理 pop 的摊还代价也是 0, 则总摊还代价的上界 (最坏情况) 为 O(n)O(n)