平摊分析是怎么一回事?:
感觉好像和当初求快速排序的平均复杂度是类似的?但其实不是:
不涉及概率的话,那就和当初求快排期望不一样了。
那么这个研究方法是什么?:聚集方法,会计方法,势能算法。
聚集方法:
这个平均是直接的,简单的,不管是哪一步。
举个例子:
现在我有一个函数:
可以看到,
现在,我们来考虑这个问题:
我们有n个操作,那么最坏复杂度是什么情况?全部都是muitpop?mulipop的复杂度最大是n,那最终T(n)=n*n?明显太大了!
所以:
全是pop push 就是n,有了mulipop就变大,最可怕的情况是没有pop,全是mulipop,那也只有2n。
另一个例子:二进制计算器:
要给x加一,也就是说,低位数1的变成0,一直往上,直到看到0,就把它变成1,然后就结束了。
但这样算也太粗糙了,我们要精细些:
我们可以发现,第一列(A[0])每次加1都要改一次,第二列每加两次就改一次,第三列每加4次就改一次····现在加了n次,那A[0]位就改了n次,A[1]位改了n/2次····可以发现
这和10进制加法是一个道理。我们给一个10进制数加1,第一位数每次都会变化,但十位要加10次才会变,百位要100次才会变。
(等等,i最大不应该是log2k吗?ppt写错了。)
总而言之,就是把总的复杂度算出来(都是可以明确算出来小于什么的)在除以总操作数。
现在来讲讲会计方法:
举个例子:
有多少push就有多少pop,因此直接给他2块钱,各式各样的Pop就不管了。
接下来开始分析:
push平摊的代价有2,因此每次都会存一点“钱”,pop的时候,就会花掉这些钱。可以发现,栈里有多少可以pop出来的元素,就会有多少钱存着,因此永远都不会破产。
还有一个例子:
这样定义是不是特别好?反正每次加1都只会发生一次0-1反转,这样好算多了。
简单说,就是:进行了n次操作,也就有n次0-1变化,交了2n块钱。当然这些钱可能没全花掉,多交了,所以说是上界为2n
势能分析:
咋一看好像和会计法没什么区别?
现在的问题是:势能函数究竟是什么鬼?:
注意:势能只要比初始值大就可以了,中间可以变小。这里因为拿“栈中剩余的元素”作为势能,和会计法的存款正好一样多,所以很像,但不一样。可以看出,势能法更加自由一些,可以设置为更大的值,只要势能不小于0就不算翻车。
下面这个例子相信你也已经猜到了:
拿1的个数作为势能函数,很好地符合了要求。可以直接在这里分析:每次加一的时候,开头的t个1变成0加上最后0变1就是全部的操作了,这时减少了1的个数t-1(减少t个1的同时又加了一个1),最后平摊的代价也就只有2.
当然假如初始值不是0怎么办?:
就是把Ci放到另一边,接下来也就是一样的(其实不换不也看得出来吗?就是把开头的势能换成b0呗):
现在再来讲一讲动态表:
这里要解释一下:空表的大小为0,规定此时装载因子为1.
现在要扩张表了怎么办?:
遇到装不下的情况就翻一倍。现在可以看到,假如表装得下,操作复杂度就是1;假如装不下,那就要是原本的元素数加1.
现在具体分析:
聚集分析:
假如要用会计法呢?:
“没有存款的数据”是什么?下面解释一下:
就是这样:每次扩张的时候,以前的老数据会花掉自己的存款,那下次扩张怎么办?就让后来的新元素来帮他们存,这样等到下次扩张的时候,以前用掉了存款的老元素存款又都回来了。这样总不会负债。
接下来就是势能法了。
这样,当没有扩表的时候,Ci=1+2=3,在扩表时Ci=size[T](之前的size)+1+(2(现在的势能)-size(T)(也是之前的))=3
刚刚一直是在扩表,现在要缩表怎么办?:
假如把界限设为1/2的话,想象一下:我刚刚扩完表,数量是1/2多一个,只要再删一次就要又缩一遍,这不神经病吗?所以要变成1/4比较好。