《算法导论》第17章:摊还分析(Amortized analysis)

基本概念

在摊还分析中,我们求数据结构的一个操作序列中所执行的所有操作的平均时间,来评价操作的代价,可以保证最坏情况下每个操作的平均性能

本文,我们将以“栈操作”为例来讲解三种摊还分析方法。

“栈操作”描述

push(s, x):将对象x压入栈S中。

pop(s):将栈S的栈顶对象弹出,并返回该对象。对空栈调用pop会产生一个错误。

multipop(s, k):循环调用pop(s),弹出栈顶的k个元素(k<n,n为栈的最大容量)。

那么,现在需要分析:执行n次栈操作最坏情况下的时间复杂度是多少?

聚合分析(Aggregate analysis)

如果对所有的n,一个n个操作的序列最坏情况下花费的总时间为T(n),那么在最坏情况下,每个操作的平均代价,或摊还代价为T(n)/n。此外,此摊还代价是适用于每个操作的,即使序列中有多种类型的操作也是如此。

对于栈操作,一个元素只能被弹出一次,对于一个空栈,执行pop的次数(包括multipop调用的)一定比执行push的次数要少。若有n次操作,则push操作的复杂度为O(n),而pop的复杂度亦为O(n),则总复杂度T(n) = O(n),摊还代价为O(1)。

核算法(Accounting method)

在核算法中,我们对不同操作赋予不同费用,称为摊还代价,可能多于或少于其实际代价。当一个操作的摊还代价超出其实际代价时,我们将差额存入数据结构中的特定对象,存入的差额称为信用。对于后续操作中摊还分析小于实际代价的情况,信用可以用来支付差额。值得注意的是,信用必须保持非负,因为摊还代价须为实际代价的上界。

对于栈操作,我们可以赋给push的摊还代价是2(自己使用1,为弹出储存1),pop、multipop的摊还代价就是0。根据我们上述待求的问题,最坏的情况就是所有的n步操作都是push,这样总代价就是2n,时间复杂度为O(n)。

势能法(Prepaid method) 

势能法其实与核算法相似。每次操作后得到的数据结构都对应一个势能Φ(i),每次的摊还代价C*(i) = C(i) + Φ(i) - Φ(i-1) = C(i) + ΔΦ需要保证Φ(i) ≥ Φ(0)总摊还代价∑C*(i) = ΣC(i) + Φ(n) - Φ(0),就是总实际代价ΣC(i)的上界

对于栈操作,令Φ(i)为栈的大小,则Φ(i) ≥ Φ(0) = 0。由摊还代价的定义可知,push的摊还代价为2,pop、multipop的摊还代价为0(弹栈势能减少,用于和弹栈操作的代价相抵消),时间复杂度依然为O(n)。

动态表

动态表的扩张:当表被填充满的时候,则创建一个大小为原来2倍的新表,并将元素依次复制过来。

动态表的删除:为防止出现插入、删除反复,而使表的大小反复变化。当装载因子α = 1/4时,才进行表的收缩

摊还代价:O(1)  --  核算法:插入操作的摊还代价为3。

 参考资料

https://blog.csdn.net/zhongkelee/article/details/44563355

http://open.163.com/movie/2010/12/A/L/M6UTT5U0I_M6V2U1OAL.html

发布了91 篇原创文章 · 获赞 142 · 访问量 9万+

猜你喜欢

转载自blog.csdn.net/leelitian3/article/details/86777393