笔试算法题(46):简介 - 二叉堆 & 二项树 & 二项堆 & 斐波那契堆

二叉堆(Binary Heap)

  • 二叉堆是完全二叉树(或者近似完全二叉树);其满足堆的特性:父节点的值>=(<=)任何一个子节点的键值,并且每个左子树或者右子树都是一
    个二叉堆(最小堆或者最大堆);一般使用数组构建二叉堆,对于array[i]而言,其左子节点为array[2*i],其右子节点为
    array[2*i+1];二叉堆支持插入,删除,查找最大(最小)键值的操作,但是合并二叉堆的复杂度较高,时间复杂度为O(N);但是二项堆或者斐波
    那契堆则仅需要O(logN);

二项树(Binomial Tree)

  • 定义度数为二项树根节点的直接子节点个数;如果一棵二项树的度数为0,则其只包含一个根节点;如果一棵二项树(包括子树)的度数为K,则其根节点包含K个子节点,并且其子节点分别为度数是K-1,K-2,K-3,…,1,0的子树的根;









  • 每当一棵二项树的度数从k-1变成k,则其所有子节点的个数增加2k-1。因此度数为K的二项树的所有子节点个数为1+2+…+2k-1=2k;

  • 二项树的高度由其增加的度数锁带来的子树的高度确定(度数每增加1,相当于二项树根节点增加一个其自身大小的子树,所以其高度和节点数都变成2N或者2H),所以其高度为H=k;

  • 在深度为h的层(从0开始记),节点个数为C(k, h),也就是从k个数中选h个数的选择方法数;C(k, h)=k!/(h!*(k-h)!);

二项堆(Binomial Heap)

  • 二项堆是一种类似于二叉堆的结构,与其相比二项堆的优势在于可以快速合并两个二项堆;二项堆H由一组二项树组成,并且满足下述性质:

  • H中每一棵二项树都满足最小堆性质(父节点的键值小于等于其子节点的键值);此性质保证每棵二项树的根节点都包含最小关键字;

  • H中不能有两棵或者以上的二项树具有相同的度数(包括0);此性质保证节点数为N的二项堆最多只有logN+1棵二项树(也就是按照1,2,22,23,…,2N排序的一组二项树);

  • 二项树的合并操作:如果合并两棵度数相同的二项树,只需比较两棵树根节点的大小,选取关键字较小的根节点所在的二项树作为主树,将剩下的一棵树作为主树根
    节点的一个子节点(由于新二项树的度数增加了1,则节点数需要增加2k,则新加入的树正好满足节点数增加的需求);时间复杂度为O(1);


  • 二项堆的合并操作:给定两个二项堆A和B,分别将A和B各自的二项树按照度数大小排序,[1,2,…,ak]和[1,2,…,bk];按照度数从小到大进行合并,如果度数i位置上,A和B集合中都有对应的二项树,则在O(1)时间内完成合并,并变成度数为i+1的二项树,然后如同加法操作的进位一样参与i+1位置上二项树的合并;

  • 如果度数i位置上,A和B集合中仅有一边有对应的二项树,则直接将这唯一的二项树放入结果集合中度数为i的位置(由于合并操作是按照度数从小到大进行,所以当前位的结果不会对之后的操作具有影响),对于具有合计节点为N的A和B的二项堆合并操作而言,其最多具有logN次O(1)时间复杂度的合并操作,所以总计的时间复杂度为O(logN);实际操
    作里面首先按照归并方式将两个二项堆的根节点组成的链表合并成为一个链表,这样就有很多度数相同的根节点,接下来的任务就是合并这些根节点度数相同的树;

  • 插入操作:无论是对于单个节点的插入,还是对于一棵二项树的插入操作而言,实际上都是一次两个二项堆的合并操作,所以时间复杂度为O(logN);对于单
    个节点的插入而言,如果没有时效性要求则可以维护一个新增节点的临时二项树(类似cache),然后一次性与原始的二项堆进行合并;

  • 查找最小关键字操作:对于一个二项堆而言,实现的时候使用链表或者数组存储各个二项树的根节点,由于二项树遵循最小堆性质,所以根节点键值就是各个二项树
    内的最小值,所以直接查询二项堆维护的链表或者数组即可,时间复杂度为O(logN);(如果根节点维护有序,则具有更好的时效性);

  • 删除最小关键字操作:首先在O(logN)时间内找到最小关键字所在的二项树;然后将此二项树从二项堆内删除,并且获取其根节点的所有子树构成一个二项堆
    (需要反转其子女的sibling指针,从而构成一个有序的二项堆),所以删除最小关键字操作最终页转换为合并两个二项堆的操作,时间复杂度仍旧为
    O(logN);

  • 减小某一个节点的关键值操作:递归与其父节点进行交换知道满足最小堆的性质,时间复杂度为O(logN);

  • 删除某一个节点的操作:首先将指定节点的关键值设置为无穷小,然后将其提升到所在二项树的根节点处,这样就转换为删除最小关键字的操作,时间复杂度为O(logN);

  • 二项堆的实现:(为了便于理解:一棵包含了13个节点的二项堆,其二项树组成为1101,也就是13的二进制表示)


 1 struct Node {
2 int key;
3 /**
4 * 当前节点作为根节点的二项树的度数
5 * */
6 int degree;
7 /**
8 * 如果是根节点,则sibling用作二项堆中串联二项树的指针
9 * 如果不是根节点,则sibling用作同一个父节点下的所有子节点
10 * */
11 Node *sibling;
12 /**
13 * 用于访问当前节点的所有子节点,此指针仅指向其子节点最左边的一个
14 * 需要在LeftMostChild节点上使用sibling才能访问其他子节点
15 * */
16 Node *LeftMostChild;
17 /**
18 * 指向父亲节点的回溯指针
19 * */
20 Node *parent;
21 };

斐波那契堆(Fibonacci Heap)

  • 一种松散的二项堆,如果不对FH做任何Decrease-Key或者Delete操作的话,FH中每棵树就跟二项树一样,但跟二项堆的不同点在于如果进行
    了上述操作,则构成FH的树可以不是二项树,并且多个树的根节点可以不用排序;FH在优势在于其建堆,插入,获取最小值,合并FH等操作都能在O(1)的
    平摊运行时间内完成(删除元素操作除外);FH的缺点在于其常数因子较大,并且程序实现较为复杂,所以实际应用多以二项堆为首选;

  • FH的策略是将堆维护工作尽量推后执行;比如当合并两个FH的时候,并不是立即执行合并操作去保证堆内的树根有序或者没有相同度数的二项树,而是留待到执行Extract-Min操作的时候才执行相同度数二项树的合并;

  • FH中由二项树根节点组成的表示一个双向循环链表,并且同一个父节点下的所有节点也构成一个双向循环链表,其他的构造与二项堆相同;

参考连接:
https://github.com/brandenburg/binomial-heaps/blob/master/iheap.h
http://www.cnblogs.com/xuqiang/archive/2011/06/01/2065549.html

http://en.wikipedia.org/wiki/Fibonacci_heap
http://mindlee.net/2011/09/29/fibonacci-heaps/

时间: 2024-10-12 15:32:12

笔试算法题(46):简介 - 二叉堆 & 二项树 & 二项堆 & 斐波那契堆的相关文章

斐波那契堆(一)之 图文解析 和 C语言的实现

概要 本章介绍斐波那契堆.和以往一样,本文会先对斐波那契堆的理论知识进行简单介绍,然后给出C语言的实现.后续再分别给出C++和Java版本的实现:实现的语言虽不同,但是原理如出一辙,选择其中之一进行了解即可.若文章有错误或不足的地方,请不吝指出! 目录1. 斐波那契堆的介绍2. 斐波那契堆的基本操作3. 斐波那契堆的C实现(完整源码)4. 斐波那契堆的C测试程序 转载请注明出处:http://www.cnblogs.com/skywang12345/p/3659060.html 更多内容:数据结

《Thinking in Algorithm》16.堆结构之斐波那契堆

堆的变体: 二叉堆 二项堆 斐波那契堆 前面的博客中我们讲到的堆的两种变体,二叉堆和二项堆,今天我们要讲的就是著名的斐波那契堆. 依然首先列出了三种堆的时间复杂的比较. 从上面能发现斐波那契堆的时间复杂度在很多操作上有优化,如insert, minimum, union , decrease-key,而extreact-min,delete没有变化. 可能看到这很多人会说为什么有这么好的斐波那契堆,我们还要去研究其他堆呢,确实,理论上来讲,对于extreact-min和delete操作想对于其他

算法导论第十九章 斐波那契堆

<算法导论>第二版中在讨论斐波那契堆之前还讨论了二项堆,但是第三版中已经把这块的内容放到思考题中,究极原因我想大概是二项堆只是个引子,目的是为了引出斐波那契堆,便于理解,而且许多经典的算法实现都是基于斐波那契堆,譬如计算最小生成树问题和寻找单源最短路径问题等,此时再把二项堆单独作为一章来讲显然没有必要.类似的堆结构还有很多,如左倾堆,斜堆,二项堆等,下次我打算开一篇博客来记录下它们的异同点. 一.摊还分析(第十七章) 这些高级的数据结构的性能分析一般是基于一个技术——摊还分析,可以理解成一种时

算法导论 第20章 斐波那契堆

一.概念 1.斐波那契堆 斐波那契堆是可合并堆 在不涉及删除的操作(除去EXTRACT和DELETE)中,操作仅需O(1)的平摊运行时间 当EXTRACT和DELETE的操作数目较小时斐波那契堆能得到较好的运行效率. 斐波那契堆不能有效地支持SEARCH操作 用于解决诸如最小生成树和寻找单源最短路径等问题的快速算法都要用到斐波那契堆. 2.斐波那契堆的结构 斐波那契堆由一组最小堆构成,这些最小堆是有根的无序树. 结点结构: key: 关键字,作为排序.判断结点大小的标准 left, right:

算法导论 第十九章:斐波拉契堆

斐波拉契堆是由一组最小堆有序树组成,每棵树遵循最小堆性质,并且每棵树都是有根而无序的.所有树的根通过left和right指针来形成一个环形的双链表,称为该堆的根表. 对于一个给定的斐波拉契堆H ,可以通过指向包含最小关键字的树根指针H.min来访问.堆中每个节点还包含x.mark,x.degree两个域,x.degree表示x的子女表中的子女个数:x.mark表示从x上次成为另一个节点子女以来是否失掉一个孩子. 斐波拉契对的结构如下: 势能函数: 可以利用势能方法来分析斐波拉契堆的性能.其势能函

优先队列——斐波那契堆

1. 引言 最近一直在写最短路径的迪杰斯特拉与双向迪杰斯特拉算法,使用优先队列可以极大的加快算法的运行效率.比如在OL数据集中,对于迪杰斯特拉算法用优先队列(二叉堆实现)代替普通的数组(数据结构书中提供的算法)快了将近60倍. 由上可得如何实现优先队列对迪杰斯特拉.双向迪杰斯特拉以及其它用到优先队列的最短路径求解算法(如reach.A*)等至关重要.另外对于一些其他用到优先队列的问题也具有相当的影响. 对于优先队列来说,只需要入队.出队即可,因为该文章只关注堆的插入(push)与删除(delet

golang 实现斐波那契堆

二叉堆提供了o(lgn) 时间的插入, 删除最小,降级等操作,o(n) 时间的合并操作;  斐波那契堆提供了更优操作时间界限:o(1) 插入, o(lgn) 删除最小, o(lgn) 删除, o(1)合并. 根据算法导论上说,斐波那契堆在删除最小或删除操作被执行次数相比其他操作少得多时,尤为适用.一些图的算法中(计算最小生成树,单源最短路径)作为基本构建块(作为优先队用). 考虑到斐波那契堆实现的复杂,可能二叉堆在实践上更具可用性.就像rob pike 在 <notes on programmi

斐波那契堆

斐波纳契堆(Fibonacci Heap)于 1984 年由 Michael L. Fredman 与 Robert E. Tarjan 提出,1987 年公开发表,名字来源于运行时分析所使用的斐波那契数. 斐波那契堆同二项堆(Binomial Heap)一样,也是一种可合并堆(Mergeable Heap).与二项堆一样,斐波那契堆是由一组最小堆有序树构成,但堆中的树并不一定是二项树.与二项堆中树都是有序的不同,斐波那契堆中的树都是有根而无序的. 实际上,斐波那契堆松散地基于二项堆.如果不对斐

[CLRS][CH 19]斐波那契堆

斐波那契堆简介 斐波那契堆(Fibnacci Heap)有两种用途:第一,支持一系列操作,这些操作构成了所谓的可合并堆.第二,其一些操作可以在常数时间内完成,这使得这种数据结构非常适合于需要频繁调用这些操作的应用. 可合并堆(Mergeable Heap)支持如下五种操作:Make-Heap(), Insert(H, x), Minmun(H), Extract-Min(H), Union(H1, H2).事实上,就是具备了快速合并操作的堆(Heap). 斐波那契堆还支持额外两种操作:Decre