前言

  斐波那契堆(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. day 75天 bbs 项目第一天 ,auth登陆

    from django.db import models from django.contrib.auth.models import AbstractUser class UserInfo(Abst ...

  2. click 版本升级7.0踩过的坑

    click 版本升级7.0踩过哪些坑? click 版本6.7升级至7.0以上,包名由 click 变更为 Click click 的 Options 和 Parameters 规则变更为如下: Fo ...

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

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

  4. 使用pipreqs生成项目依赖

    作用 导出当前项目的环境依赖 使用 # 安装 pip3 install pipreqs # 导出项目环境依赖 pipreqs ./ # 如果是Windows系统,会报编码错误 (UnicodeDeco ...

  5. Binary Indexed Tree-307. Range Sum Query - Mutable

    Given an integer array nums, find the sum of the elements between indices i and j (i ≤ j), inclusive ...

  6. hdoj1068 Girls and Boys(二分图的最大独立集)

    题意:有n个人,要彼此认识.选择一个集合,使得集合里的每个人相互不认识.求集合中人数的最大值. 求二分图的最大独立集. 公式:最大独立集=顶点数-最大匹配 这个题目中因为集合是一个,所以求出最大匹配数 ...

  7. servlet中request和response

    一.HttpServletRequest介绍 HttpServletRequest对象代表客户端的请求,当客户端通过HTTP协议访问服务器时,HTTP请求头中的所有信息都封装在这个对象中,通过这个对象 ...

  8. java中的IO流(输入流与输出流)概述与总结

    Java中IO流,输入输出流概述与总结 总结的很粗糙,以后时间富裕了好好修改一下. 1:Java语言定义了许多类专门负责各种方式的输入或者输出,这些类都被放在java.io包中.其中, 所有输入流类都 ...

  9. day 42 mysql 数据库(2)

    前情提要: 本节继续学习数据库   一:ddl 创建表 >字段名 >数据类型 >约束规则 >显示建表语句 修改表: 二:数据类型 >数值类型 >小数类型 >字 ...

  10. yii2之ActiveForm表单使用

    因目前项目并非前后端分离模式,且用到PHP的yii2框架(所有html代码,js较多内嵌在.php文件内多少采用同步提交[喷墨中...]),遂对于前端面上需要用到的yii2小组件一些整理(因是前端若涉 ...