笔试算法题(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的二进制表示)
struct Node {
int key;
/**
* 当前节点作为根节点的二项树的度数
* */
int degree;
/**
* 如果是根节点,则sibling用作二项堆中串联二项树的指针
* 如果不是根节点,则sibling用作同一个父节点下的所有子节点
* */
Node *sibling;
/**
* 用于访问当前节点的所有子节点,此指针仅指向其子节点最左边的一个
* 需要在LeftMostChild节点上使用sibling才能访问其他子节点
* */
Node *LeftMostChild;
/**
* 指向父亲节点的回溯指针
* */
Node *parent;
};
斐波那契堆(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/
笔试算法题(46):简介 - 二叉堆 & 二项树 & 二项堆 & 斐波那契堆的更多相关文章
- 斐波那契堆(二)之 C++的实现
概要 上一章介绍了斐波那契堆的基本概念,并通过C语言实现了斐波那契堆.本章是斐波那契堆的C++实现. 目录1. 斐波那契堆的介绍2. 斐波那契堆的基本操作3. 斐波那契堆的C++实现(完整源码)4. ...
- 斐波那契堆(一)之 图文解析 和 C语言的实现
概要 本章介绍斐波那契堆.和以往一样,本文会先对斐波那契堆的理论知识进行简单介绍,然后给出C语言的实现.后续再分别给出C++和Java版本的实现:实现的语言虽不同,但是原理如出一辙,选择其中之一进行了 ...
- 斐波那契堆(三)之 Java的实现
概要 前面分别通过C和C++实现了斐波那契堆,本章给出斐波那契堆的Java版本.还是那句老话,三种实现的原理一样,择其一了解即可. 目录1. 斐波那契堆的介绍2. 斐波那契堆的基本操作3. 斐波那契堆 ...
- fibonacci-Heap(斐波那契堆)原理及C++代码实现
斐波那契堆是一种高级的堆结构,建议与二项堆一起食用效果更佳. 斐波那契堆是一个摊还性质的数据结构,很多堆操作在斐波那契堆上的摊还时间都很低,达到了θ(1)的程度,取最小值和删除操作的时间复杂度是O(l ...
- 基于visual Studio2013解决算法导论之045斐波那契堆
题目 斐波那契堆 解决代码及点评 // 斐波那契堆.cpp : 定义控制台应用程序的入口点. // #include<iostream> #include<cstdio> ...
- 斐波那契堆(Fibonacci heap)原理详解(附java代码实现)
前言 斐波那契堆(Fibonacci heap)是计算机科学中最小堆有序树的集合.它和二项式堆有类似的性质,但比二项式堆有更好的均摊时间.堆的名字来源于斐波那契数,它常用于分析运行时间. 堆结构介绍 ...
- 笔试算法题(40):后缀数组 & 后缀树(Suffix Array & Suffix Tree)
议题:后缀数组(Suffix Array) 分析: 后缀树和后缀数组都是处理字符串的有效工具,前者较为常见,但后者更容易编程实现,空间耗用更少:后缀数组可用于解决最长公共子串问题,多模式匹配问题,最长 ...
- 笔试算法题(50):简介 - 广度优先 & 深度优先 & 最小生成树算法
广度优先搜索&深度优先搜索(Breadth First Search & Depth First Search) BFS优缺点: 同一层的所有节点都会加入队列,所以耗用大量空间: 仅能 ...
- [莫队算法 线段树 斐波那契 暴力] Codeforces 633H Fibonacci-ish II
题目大意:给出一个长度为n的数列a. 对于一个询问lj和rj.将a[lj]到a[rj]从小到大排序后并去重.设得到的新数列为b,长度为k,求F1*b1+F2*b2+F3*b3+...+Fk*bk.当中 ...
随机推荐
- 二进制读取 jdbc
package com.itheima.clob.test; import java.io.FileInputStream; import java.io.FileOutputStream; impo ...
- NewStar 信息分发系统设计
目录 NewStar 信息分发系统设计 我想要怎么做 系统流程图 解释和初步的模板展示 NewStar 信息分发系统设计 我们在全世界发布网站本质就是向全世界分发我们的信息给客户/潜在客户,然后希望促 ...
- 敏捷开发(Agile development)
敏捷开发(Agile development) 敏捷开发是一种以人为核心.迭代.循序渐进的开发方法.在敏捷开发中,软件项目的构建被切分成多个子项目,各个子项目的成果都经过测试,具备集成和可运行的特征. ...
- 利用rand7()构造rand10()
题意 已知有个rand7()的函数,返回1到7随机自然数,让利用这个rand7()构造rand10() 随机1~10 参考代码 int rand7() { srand((int)time(NULL)) ...
- 傻瓜式的go modules的讲解和代码,及gomod能不能引入另一个gomod和gomod的use of internal package xxxx not allowed
一 国内关于gomod的文章,哪怕是使用了百度 -csdn,依然全是理论,虽然golang的使用者大多是大神但是也有像我这样的的弱鸡是不是? 所以,我就写个傻瓜式教程了. github地址:https ...
- 【Java】深入理解Java中的spi机制
深入理解Java中的spi机制 SPI全名为Service Provider Interface是JDK内置的一种服务提供发现机制,是Java提供的一套用来被第三方实现或者扩展的API,它可以用来启用 ...
- 《Head First 设计模式》之装饰者模式——饮料加工
装饰者模式(Decorator) ——动态地将责任附加到对象上.若要扩展功能,装饰者提供了比继承更有弹性的替代方案. 特点:建立拥有共同超类的装饰者与被装饰者来实现功能的动态扩展 原则:对扩展开放,对 ...
- linux服务器安装nodejs运行环境
安装nodejs运行环境 第一步:到node官网下载相应版本的安装包,将安装包放置服务器上,路径为 usr/local/node(可根据自身情况进行修改) 第二步:解压 ***.tar.xz格式文件需 ...
- Maven下 SpringMvn+thymeleaf 搭建
1.首先新建一个项目 2.根据以下选项,点击下一步 3.随便输入 4.配置maven的路径 5.点击完成 6.等待所有maven的库文件下载完成后配置pom.xml依赖 <dependency& ...
- Vue.js(2.x)之Class 与 Style 绑定
1.前面看数据绑定时还很困惑v-bind处理class时可以使用json格式的值,为什么换成id.href等其他属性就不行.看了下文档解释后明白了些: 2.对象语法主要有以下三种形式: 1)直接在v- ...