斐波那契数列、堆

来源:互联网 发布:access2007数据库引擎 编辑:程序博客网 时间:2024/06/06 03:01
斐波那契数列指的是这样一个数列 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144
第0项是0,第1项是第一个1。这个数列从第二项开始,每一项都等于前两项之和。
在数学上,斐波纳契数列被以递归的方法定义:F0=0,F1=1,Fn=F(n-1)+F(n-2)(n>=2,n∈N*)
性质:
1、斐波那契数列又称黄金分割数列,原因是当n趋于无穷大时,后一项与前一项的比值接近1.618,也就是小数部分等于0.618,或者前一项与后一项的比值接近0.618。
2、从第二项开始,每个奇数项的平方都比前后两项之积多1,每个偶数项的平方都比前后两项之积少1。
求斐波那契数列算法可看笔记“递归与循环”,尽量用循环来实现(效率高)!!!
应用例子:
1、有一根长为L的铁丝截成k段,每段长度不小于1(可以为小数),并且任意三段都不能拼成三角形,求k最大值。
先把截断的铁丝排序:n1<=n2<=n3<=....<=nk,
能拼成三角形等价于3条铁丝中任2条的和大于第3条,也就是两条小的边的和要大于第3条边,所以,不能拼成三角形,意味着任2条小边的和不能大于比它们大的边,也就是ni+n(i+1)<=n(i+2),因为如果ni+n(i+1)比n(i+2)小,那ni+n(i+1)肯定比n(i+2)后面的边要小,同样如果n(i+2)比ni+n(i+1)大,那一定前面任两条边的和要大。
所以题目中的条件转化为:
n1+n2<=n3;n2+n3<=n4;....;n(k-2)+n(k-1)<=nk,且n1+n2+n3+...+nk=L,
要想分成更多段,那n1首先要尽量小,取1,n2也取1,n3也尽量小,取2,以此类推,化成了
n1=1,n2=1,n(i+2)=n(i+1)+ni
也就是按照斐波那契数列关系分割铁丝
2、有一个n级台阶,每步可以走1级或2级,求走完这n级台阶一共有多少种走法。
n=1:1种走法;
n=2:2种走法;
n>2:
如果第一步走1级,则还剩下n-1级,如果第一步走2级,则还剩下n-2级,总的走法等于这两者相加,设剩下n-1级共有f(n-1)种走法,剩下n-2级共有f(n-2)种走法,则f(n)=f(n-1)+f(n-2)。
可见满足斐波那契数列:1,2,3,5,8,13....(从1开始,且第2个是2,不是1),可用循环来求。


斐波那契堆

斐波那契堆(Fibonacci heap)是堆中一种,它和二项堆一样,也是一种可合并堆;可用于实现合并优先队列。斐波那契堆比二项堆具有更好的平摊分析性能,它的合并操作的时间复杂度是O(1)。


与二项堆一样,它也是由一组堆最小有序树组成,并且是一种可合并堆。

与二项堆不同的是,斐波那契堆中的树不一定是二项树;而且二项堆中的树是有序排列的,但是斐波那契堆中的树都是有根而无序的

\

插入操作:

插入操作非常简单:插入一个节点到堆中,直接将该节点插入到'根链表的min节点'之前即可;若被插入节点比'min节点'小,则更新'min节点'为被插入节点。斐波那契堆的根链表是'双向链表',这里将min节点看作双向联表的表头。在插入节点时,每次都是将节点插入到min节点之前(即插入到双链表末尾)。
\

合并操作:

合并操作和插入操作的原理非常类似:将一个堆的根链表插入到另一个堆的根链表上即可。简单来说,就是将两个双链表拼接成一个双向链表。所以斐波那契堆的合并操作时间复杂度是一个常数,故为O(1)。

取出最小节点:

抽取最小结点的操作是斐波那契堆中较复杂的操作。


1、将要抽取最小结点的子树都直接串联在根表中;

2、合并所有degree相等的树,直到没有相等的degree的树;

减小节点值:

减少斐波那契堆中的节点的键值,这个操作的难点是:如果减少节点后破坏了'最小堆'性质,如何去维护呢?下面对一般性情况进行分析。


1、首先,将'被减小节点'从'它所在的最小堆'剥离出来;然后将'该节点'关联到'根链表'中。 倘若被减小的节点不是单独一个节点,而是包含子树的树根。则是将以'被减小节点'为根的子树从'最小堆'中剥离出来,然后将该树关联到根链表中。

2、接着,对'被减少节点'的原父节点进行'级联剪切'。所谓'级联剪切',就是在被减小节点破坏了最小堆性质,并被切下来之后;再从'它的父节点'进行递归级联剪切操作。而级联操作的具体动作则是:若父节点(被减小节点的父节点)的marked标记为false,则将其设为true,然后退出。否则,将父节点从最小堆中切下来(方式和'切被减小节点的方式'一样);然后递归对祖父节点进行'级联剪切'。marked标记的作用就是用来标记'该节点的子节点是否有被删除过',它的作用是来实现级联剪切。而级联剪切的真正目的是为了防止'最小堆'由二叉树演化成链表。

2、最后,别忘了对根链表的最小节点进行更新。
\
增加节点值:

增加节点值和减少节点值类似,这个操作的难点也是如何维护'最小堆'性质。思路如下:


1、将'被增加节点'的'左孩子和左孩子的所有兄弟'都链接到根链表中。

2、接下来,把'被增加节点'添加到根链表;但是别忘了对其进行级联剪切。</p>
\

删除节点:

删除节点,采用操作是:'取出最小节点'和'减小节点值'的组合。


(1) 先将被删除节点的键值减少。减少后的值要比'原最小节点的值'即可。

(2) 接着,取出最小节点即可。
0 0
原创粉丝点击