前言

  斐波那契堆(Fibonacci heap)是计算机科学中最小堆有序树的集合。它和二项式堆有类似的性质,但比二项式堆有更好的均摊时间。堆的名字来源于斐波那契数,它常用于分析运行时间。

堆结构介绍

  基本术语介绍:

  关键字:堆节点储存的用于比较的信息

  度数:堆节点拥有的孩子数(注意,不包括孩子的孩子)

  左兄弟:节点左边的兄弟节点

  右兄弟:节点右边的兄弟节点

  mark:是否有孩子节点被删除

  斐波那契堆是一系列无序树的集合,每棵树是一个最小堆,满足最小堆的性质。(注意,树是无序的,所以不要纠结树该怎么排序)。堆保存了堆中所有节点的数目,保存了最小关键字的节点(这是整个堆的唯一入口,根据这个最小节点可以获取整个堆的任何节点)。

  堆的节点是堆的最小单位,它是双向链表的节点,意味着它保存了上下节点的信息,如下图,(也能看出树的根节点排列是无序的)。

  

  它主要有如下性质:

  1、关键字

  2、度数

  3、左兄弟

  4、右兄弟

  5、父节点

  6、孩子节点(任一个孩子节点,随意)

堆基本操作

  一、插入操作

    1、创建一个节点,如21

  

  2、把新建的节点插入到根链表中,如果是最小值,则更新它为堆的最小节点。插入位置没有规定,一般习惯插入到min的左边。把堆的“所有节点数”值加1

  

  3、插入操作完成了(插入并不会对堆进行修改,修改是在其他操作中进行的,所以比较简单)

  二、删除最小节点

    1、删除最小节点,并把它的所有孩子合并到堆的根链表中,并更新min

  2、合并根节点的树,使任何树的度(rank)不相等

    观察到7有1个孩子节点,即度为1,先保存起来,由于是初始的,肯定没有和7度相同的

    接着下一个根节点24,度为2,继续。

    23, 度为1,继续

    17, 度为1。 由于已经有度为1的根节点了,所以需要合并这两个节点

    根据最小堆得性质,把23合并到17上,作为17的孩子节点

    此时17的度为2,仍然重复,继续合并,直到没有度一样的根节点

    最终结果如下图

    

  三、减小key值

    如果没有违背最小堆的性质,直接减小key的值

    否则,把以key为根节点的树合并到堆的根链表中

    如果有一个节点有两个孩子移除了,把这个节点也合并到根链表中,并且unmark它

    

    现在举一个例子来说明各种可能情况

    1、不违反最小堆性质

      把46减小为29,不违反最小堆性质,不改变堆结构

  

    2、违反最小堆性质,合并到根链表中,并且unmark 它

      把29减小为15,违反了堆性质

  

    把15合并到根链表中

  如果父节点没有mark(没有失去孩子), 设置它为mark

  

  如果父节点已经是mark,则把父节点合并到根链表中,并设置为unmark。

  把节点35减小到5 

  

  由于违反了,把5合并到根

  由于26已经mark,把26这个子树合并到根

  同理24合并到根

  由于7已经是根节点了,停止,全部结束

  四、删除节点

    删除节点比较简单,主要分为两步

    1、把节点值decrease比堆最小值还小

    2、删除最小值

java代码实现(仅供参考,逻辑并不十分严谨)

 public class FibonNode<T extends Comparable<T>> {

     public T key;

     public FibonNode<T> child;

     public FibonNode<T> left;

     public FibonNode<T> right;

     public boolean mark;

     public FibonNode<T> parent;

     public int degree;

     public FibonNode(T key){
this.degree = 0;
this.key = key;
this.parent = null;
this.child = null;
this.left = null;
this.right = null;
this.mark = false;
}
}
 public class FibonHeap<T extends Comparable<T>> {

     private int keyNum;

     private FibonNode<T> min;

     /*
* 保存当前指针
*/
private FibonNode<T> current; /*
* 保存各个度对应的节点,如度为1的节点对应的节点
*/
private Map<Integer, FibonNode<T>> degreeNodes; public FibonHeap(T key) {
min = new FibonNode<T>(key);
keyNum += 1;
min.left = min;
min.right = min;
} /*
* 插入值
*/
public void insert(T key) {
FibonNode<T> node = new FibonNode<T>(key);
insert(node);
} /*
* 删除最小值
*/
public void deleteMin() {
degreeNodes = new HashMap<Integer, FibonNode<T>>();
removeMinNode();
consolidate(); } /*
* 删除节点
*/
public void deleteNode(FibonNode<T> node){
T everSmall = null;
decrease(node, everSmall);
deleteMin();
} /*
* 合并堆
*/
public FibonHeap<T> union(FibonHeap<T> heapA, FibonHeap<T> heapB){
FibonNode<T> minA = heapA.min;
FibonNode<T> minB = heapB.min;
minA.right = minB;
minA.right.left = minB.right;
minB.left = minA;
minB.right.left = minA.right;
FibonNode<T> min = minA;
if(minB.key.compareTo(minB.key) < 0){
min = minB;
}
heapA.min = min;
heapA.keyNum += heapB.keyNum;
return heapA;
} private void insert(FibonNode<T> node) {
//插入就是直接更新左右节点就可以了
min.left.right = node;
node.left = min.left;
node.right = min;
min.left = node;
T minKey = min.key;
if (node.key.compareTo(minKey) < 0) {
min = node;
}
keyNum += 1;
} /*
* 把节点从堆中删除
*/
private void removeMinNode() {
FibonNode<T> left = min.left;
if (left == min) {
//说明只剩最后一个节点了,也就是最小节点自己
if (min.child != null) {
min = null;//这里不是null,应该是孩子节点中最小节点,笔者没有写完而已
}
} else {
deleteInList(min);
addChToR(min);
min = left; // 先随意选个节点作为最小节点,在随后环节会更新的
}
keyNum--;
} /*
* 把根节点合并使其所有节点的度不相等
*/
private void consolidate() {
current = min;
do {
current = putDegreeNodes(current);
if (current.key.compareTo(min.key) < 0) {
min = current;
}
current = current.right;
} while (current != min && current.left != current);
} /*
*
*/
private FibonNode<T> putDegreeNodes(FibonNode<T> node) {
int nodeDegree = node.degree;
//从map中找节点对应的度是否存在,存在说明有相同度的节点了,需要合并
FibonNode<T> nodeInMap = degreeNodes.get(nodeDegree);
if (nodeInMap == null) {
degreeNodes.put(nodeDegree, node);
} else {
if (node.key.compareTo(nodeInMap.key) < 0) {
deleteInList(nodeInMap);
nodeInMap.left = nodeInMap;
nodeInMap.right = nodeInMap;
node = fibLink(node, nodeInMap);
nodeInMap = node;
} else {
deleteInList(node);
node.left = node;
node.right = node;
nodeInMap = fibLink(nodeInMap, node); node = nodeInMap;
}
degreeNodes.put(nodeDegree, null);
node = putDegreeNodes(node);
}
return node;
} private FibonNode<T> fibLink(FibonNode<T> parent, FibonNode<T> child) {
if (parent.child == null) {
parent.child = child; } else {
parent.child = insertCyle(parent.child, child);
}
child.parent = parent;
parent.degree += 1;
return parent;
} /*
* 从所在链中删除
*/
private void deleteInList(FibonNode<T> node) {
FibonNode<T> left = node.left;
FibonNode<T> right = node.right;
left.right = right;
right.left = left;
} /*
* 插入到链中
*/
private FibonNode<T> insertCyle(FibonNode<T> target, FibonNode<T> node) {
FibonNode<T> left = target.left;
left.right = node;
node.left = target;
node.right = target;
target.left = node;
return target;
} /*
* 把孩子节点添加到根链表中
*/
private void addChToR(FibonNode<T> node) {
FibonNode<T> aChild = node.child;
if (aChild == null) {
return;
}
do {
//孩子节点循环插入根
FibonNode<T> right = aChild.right;
min.right = insertCyle(min.right, aChild);
aChild = right; } while (aChild != node.child);
} public void decrease(FibonNode<T> target, T key){
FibonNode<T> parent = target.parent;
if(target.key.compareTo(key) < 0){
System.out.println("只能减少key值");
return;
}
if(parent == null){
//如果修改节点是根节点,直接修改
target.key = key;
if(key.compareTo(min.key) < 0){
//更新最小节点
min = target;
}
return;
}
if(parent.key.compareTo(key) < 0){
//如果值修改稿后不违反最小堆,直接修改即可
target.key = key;
return;
}
cutAndMeld(target);
parent = cascadingCut(parent);
} /*
* 删除节点,并合并到根中
*/
private void cutAndMeld(FibonNode<T> target){
target.parent = null;
target.mark = false;
insert(target);
} /*
* 级联删除,使其符合斐波那契堆性质
*/
private FibonNode<T> cascadingCut(FibonNode<T> parent){
if(null == parent){
return null;
}
parent.degree--;
if(parent.mark == false){
parent.mark = true;
}else{
cutAndMeld(parent);
parent = cascadingCut(parent);
}
return parent;
} }

参考文献

  http://staff.ustc.edu.cn/~csli/graduate/algorithms/book6/chap21.htm

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

  fibonacci-heap

  Fibonacci_heap

  

斐波那契堆(Fibonacci heap)原理详解(附java代码实现)的更多相关文章

  1. LeetCode 873. 最长的斐波那契子序列的长度 题目详解

    题目详情 如果序列 X_1, X_2, ..., X_n 满足下列条件,就说它是 斐波那契式 的: n >= 3 对于所有 i + 2 <= n,都有 X_i + X_{i+1} = X_ ...

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

    概要 本章介绍斐波那契堆.和以往一样,本文会先对斐波那契堆的理论知识进行简单介绍,然后给出C语言的实现.后续再分别给出C++和Java版本的实现:实现的语言虽不同,但是原理如出一辙,选择其中之一进行了 ...

  3. 斐波那契堆(二)之 C++的实现

    概要 上一章介绍了斐波那契堆的基本概念,并通过C语言实现了斐波那契堆.本章是斐波那契堆的C++实现. 目录1. 斐波那契堆的介绍2. 斐波那契堆的基本操作3. 斐波那契堆的C++实现(完整源码)4.  ...

  4. 斐波那契堆(三)之 Java的实现

    概要 前面分别通过C和C++实现了斐波那契堆,本章给出斐波那契堆的Java版本.还是那句老话,三种实现的原理一样,择其一了解即可. 目录1. 斐波那契堆的介绍2. 斐波那契堆的基本操作3. 斐波那契堆 ...

  5. fibonacci-Heap(斐波那契堆)原理及C++代码实现

    斐波那契堆是一种高级的堆结构,建议与二项堆一起食用效果更佳. 斐波那契堆是一个摊还性质的数据结构,很多堆操作在斐波那契堆上的摊还时间都很低,达到了θ(1)的程度,取最小值和删除操作的时间复杂度是O(l ...

  6. 基于visual Studio2013解决算法导论之045斐波那契堆

     题目 斐波那契堆 解决代码及点评 // 斐波那契堆.cpp : 定义控制台应用程序的入口点. // #include<iostream> #include<cstdio> ...

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

    二叉堆(Binary Heap) 二叉堆是完全二叉树(或者近似完全二叉树):其满足堆的特性:父节点的值>=(<=)任何一个子节点的键值,并且每个左子树或者右子树都是一 个二叉堆(最小堆或者 ...

  8. 第2章 数字之魅——斐波那契(Fibonacci)数列

    斐波那契(Fibonacci)数列 问题描述 递归算法: package chapter2shuzizhimei.fibonacci; /** * Fibonacci数列递归求解 * @author ...

  9. python实现斐波那契数列(Fibonacci sequence)

    使用Python实现斐波那契数列(Fibonacci sequence) 斐波那契数列形如 1,1,2,3,5,8,13,等等.也就是说,下一个值是序列中前两个值之和.写一个函数,给定N,返回第N个斐 ...

随机推荐

  1. 【2019】OCP 12c 062题库更新大量新题-7

    7.daily_ords_lst is created in locally managed tablespace ORDERS_TBS which uses automatic segment sp ...

  2. BZOJ 1008--[HNOI2008]越狱(容斥&快速幂)

    1008: [HNOI2008]越狱 Time Limit: 1 Sec  Memory Limit: 162 MBSubmit: 12593  Solved: 5439[Submit][Status ...

  3. jzoj4424

    20%:暴力枚舉每一條邊有沒有被選到,然後使用并查集判斷聯通性 這樣子有20分,但是我考試寫掛了所以1分也沒有 100%:這道題2000的數據範圍,使用指數級搜索會tle,需要更加好的方法 這道題中, ...

  4. Flask的WTforms

    一.简单介绍 WTForms是一个支持多个web框架的form组件,主要用于对用户请求数据进行验证. 类似于Django中的modelform 安装: pip3 install wtforms 二.简 ...

  5. Kafka数据可靠性与一致性解析

    Partition Recovery机制 每个Partition会在磁盘记录一个RecoveryPoint, 记录已经flush到磁盘的最大offset.broker fail 重启时,会进行load ...

  6. nginx 开启GZIP、域名指向index.html

    nginx 虽然默认开启了gzip压缩,但是有关压缩文件.压缩效率没有开启,在建设我的(个人博客)[www.fayinme.cn]中,直观的感受到gzip带来的访问速度提升的快感. 如何开启GZIP ...

  7. Java线程代码实现

    线程的Java实现 参考博客:(http://www.importnew.com/20672.html) 1.继承Thread 声明Thread的子类; 这种方法是创建类继承Thread,然后重写Th ...

  8. easyUI的分页,只显示第X 共Y页。改为显示 第X 页 共Y页

    如下图,easyUI的分页,只显示第X 共Y页. 需求需要显示 第X 页 共Y页. 解决办法:在easyui-lang-zh_CN.js更改以下代码,即可.也就是在 “共{pages}页”前面加个 “ ...

  9. 解决Mysql Workbench的Error Code: 1175错误 无法删除数据

    使用workbench,如果你要批量更新或删除数据,一般会报“ Error Code: 1175 You are using safe update mode and you tried to upd ...

  10. Ubuntu链接库查找

    apt-get install apt-file apt-get update apt-file search